import SwiftUI

public extension LinearGradient {
    /// Creates a linear gradient with a specified number of color stops, applying a custom easing function to the interpolation between the start and end colors.
    /// Great for creating smooth and visually appealing gradients.
    /// - Parameters:
    ///   - startColor: The starting color of the gradient.
    ///   - endColor: The ending color of the gradient.
    ///   - n: The number of color stops in the gradient. Must be greater than
    ///   1. If `n` is less than or equal to 1, a gradient with just the start and end colors is returned.
    ///   - startPoint: The starting point of the gradient. Default is `.top`.
    ///   - endPoint: The ending point of the gradient. Default is `.bottom`.
    ///   - easing: A closure that takes a `CGFloat` value between 0 and 1 and returns a `CGFloat` value between 0 and 1, representing the eased interpolation
    static func eased(
        startColor: Color,
        endColor: Color,
        stops n: Int,
        startPoint: UnitPoint = .top,
        endPoint: UnitPoint = .bottom,
        easing: (CGFloat) -> CGFloat
    ) -> LinearGradient {
        guard n > 1 else {
            return LinearGradient(colors: [startColor, endColor], startPoint: startPoint, endPoint: endPoint)
        }

        let startColorComponents = startColor.components
        let endColorComponents = endColor.components

        let step = 1.0 / CGFloat(n - 1)
        let stops: [Gradient.Stop] = (0..<n).map { index in
            let t = CGFloat(index) * step
            let easedT = easing(t)

            let interpolatedColor = Color(
                red: Double(startColorComponents.red + (endColorComponents.red - startColorComponents.red) * easedT),
                green: Double(startColorComponents.green + (endColorComponents.green - startColorComponents.green) * easedT),
                blue: Double(startColorComponents.blue + (endColorComponents.blue - startColorComponents.blue) * easedT),
                opacity: Double(startColorComponents.opacity + (endColorComponents.opacity - startColorComponents.opacity) * easedT)
            )

            return .init(color: interpolatedColor, location: t)
        }

        return LinearGradient(
            gradient: Gradient(stops: stops),
            startPoint: startPoint,
            endPoint: endPoint
        )
    }
}

private extension Color {
    var components: (red: CGFloat, green: CGFloat, blue: CGFloat, opacity: CGFloat) {
        var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
        UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a)
        return (r, g, b, a)
    }
}
