import UIKit

// MARK: - Glass Image Generation (Telegram iOS parity)
//
// Port of GlassBackgroundView.generateLegacyGlassImage and generateForegroundImage
// from Telegram-iOS GlassBackgroundComponent.swift.

enum GlassImageGeneration {

    // MARK: - Badge Path (asymmetric rounded rect)

    private static func addBadgePath(in context: CGContext, rect: CGRect) {
        context.saveGState()
        context.translateBy(x: rect.minX, y: rect.minY)
        context.scaleBy(x: rect.width / 78.0, y: rect.height / 78.0)

        context.move(to: CGPoint(x: 0, y: 39))
        context.addCurve(to: CGPoint(x: 39, y: 0), control1: CGPoint(x: 0, y: 17.4609), control2: CGPoint(x: 17.4609, y: 0))
        context.addLine(to: CGPoint(x: 42, y: 0))
        context.addCurve(to: CGPoint(x: 78, y: 36), control1: CGPoint(x: 61.8823, y: 0), control2: CGPoint(x: 78, y: 16.1177))
        context.addLine(to: CGPoint(x: 78, y: 39))
        context.addCurve(to: CGPoint(x: 39, y: 78), control1: CGPoint(x: 78, y: 60.5391), control2: CGPoint(x: 60.5391, y: 78))
        context.addLine(to: CGPoint(x: 36, y: 78))
        context.addCurve(to: CGPoint(x: 0, y: 42), control1: CGPoint(x: 16.1177, y: 78), control2: CGPoint(x: 0, y: 61.8823))
        context.addLine(to: CGPoint(x: 0, y: 39))
        context.closePath()

        context.restoreGState()
    }

    // MARK: - Legacy Glass Image

    static func generateLegacyGlassImage(
        size: CGSize,
        inset: CGFloat,
        borderWidthFactor: CGFloat = 1.0,
        isDark: Bool,
        fillColor: UIColor
    ) -> UIImage {
        var size = size
        if size == .zero {
            size = CGSize(width: 2.0, height: 2.0)
        }
        let innerSize = size
        size.width += inset * 2.0
        size.height += inset * 2.0

        return UIGraphicsImageRenderer(size: size).image { ctx in
            let context = ctx.cgContext

            context.clear(CGRect(origin: .zero, size: size))

            let addShadow: (CGContext, Bool, CGPoint, CGFloat, CGFloat, UIColor, CGBlendMode) -> Void = { context, isOuter, position, blur, spread, shadowColor, blendMode in
                var blur = blur

                if isOuter {
                    blur += abs(spread)

                    context.beginTransparencyLayer(auxiliaryInfo: nil)
                    context.saveGState()
                    defer {
                        context.restoreGState()
                        context.endTransparencyLayer()
                    }

                    let spreadRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize).insetBy(dx: 0.25, dy: 0.25)
                    let spreadPath = UIBezierPath(
                        roundedRect: spreadRect,
                        cornerRadius: min(spreadRect.width, spreadRect.height) * 0.5
                    ).cgPath

                    context.setShadow(offset: CGSize(width: position.x, height: position.y), blur: blur, color: shadowColor.cgColor)
                    context.setFillColor(UIColor.black.withAlphaComponent(1.0).cgColor)
                    context.addPath(spreadPath)
                    context.fillPath()

                    let cleanRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize)
                    let cleanPath = UIBezierPath(
                        roundedRect: cleanRect,
                        cornerRadius: min(cleanRect.width, cleanRect.height) * 0.5
                    ).cgPath
                    context.setBlendMode(.copy)
                    context.setFillColor(UIColor.clear.cgColor)
                    context.addPath(cleanPath)
                    context.fillPath()
                    context.setBlendMode(.normal)
                } else {
                    let image = UIGraphicsImageRenderer(size: size).image(actions: { ctx in
                        let context = ctx.cgContext

                        context.clear(CGRect(origin: .zero, size: size))
                        let spreadRect = CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize).insetBy(dx: -spread - 0.33, dy: -spread - 0.33)

                        context.setShadow(offset: CGSize(width: position.x, height: position.y), blur: blur, color: shadowColor.cgColor)
                        context.setFillColor(shadowColor.cgColor)
                        let enclosingRect = spreadRect.insetBy(dx: -10000.0, dy: -10000.0)
                        context.addPath(UIBezierPath(rect: enclosingRect).cgPath)
                        addBadgePath(in: context, rect: spreadRect)
                        context.fillPath(using: .evenOdd)
                    })

                    UIGraphicsPushContext(context)
                    image.draw(in: CGRect(origin: .zero, size: size), blendMode: blendMode, alpha: 1.0)
                    UIGraphicsPopContext()
                }
            }

            // Outer shadows
            addShadow(context, true, CGPoint(), 30.0, 0.0, UIColor(white: 0.0, alpha: 0.045), .normal)
            addShadow(context, true, CGPoint(), 20.0, 0.0, UIColor(white: 0.0, alpha: 0.01), .normal)

            var a: CGFloat = 0.0
            var b: CGFloat = 0.0
            var s: CGFloat = 0.0
            fillColor.getHue(nil, saturation: &s, brightness: &b, alpha: &a)

            let innerImage = UIGraphicsImageRenderer(size: size).image { ctx in
                let context = ctx.cgContext

                context.setFillColor(fillColor.cgColor)
                let ellipseRect = CGRect(origin: .zero, size: size).insetBy(dx: inset, dy: inset)
                context.fillEllipse(in: ellipseRect)

                let lineWidth: CGFloat = (isDark ? 0.8 : 0.8) * borderWidthFactor
                let strokeColor: UIColor
                let blendMode: CGBlendMode
                let baseAlpha: CGFloat = isDark ? 0.3 : 0.6

                if s == 0.0 && abs(a - 0.7) < 0.1 && !isDark {
                    blendMode = .normal
                    strokeColor = UIColor(white: 1.0, alpha: baseAlpha)
                } else if s <= 0.3 && !isDark {
                    blendMode = .normal
                    strokeColor = UIColor(white: 1.0, alpha: 0.7 * baseAlpha)
                } else if b >= 0.2 {
                    let maxAlpha: CGFloat = isDark ? 0.7 : 0.8
                    blendMode = .overlay
                    strokeColor = UIColor(white: 1.0, alpha: max(0.5, min(1.0, maxAlpha * s)) * baseAlpha)
                } else {
                    blendMode = .normal
                    strokeColor = UIColor(white: 1.0, alpha: 0.5 * baseAlpha)
                }

                context.setStrokeColor(strokeColor.cgColor)
                var strokeEllipseRect = CGRect(origin: .zero, size: size).insetBy(dx: inset, dy: inset)
                context.addEllipse(in: strokeEllipseRect)
                context.clip()

                strokeEllipseRect = CGRect(origin: .zero, size: size).insetBy(dx: inset, dy: inset).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5)

                context.setBlendMode(blendMode)

                let radius = strokeEllipseRect.height * 0.5
                let smallerRadius = radius - lineWidth * 1.33
                context.move(to: CGPoint(x: strokeEllipseRect.minX, y: strokeEllipseRect.minY + radius))
                context.addArc(tangent1End: CGPoint(x: strokeEllipseRect.minX, y: strokeEllipseRect.minY), tangent2End: CGPoint(x: strokeEllipseRect.minX + radius, y: strokeEllipseRect.minY), radius: radius)
                context.addLine(to: CGPoint(x: strokeEllipseRect.maxX - smallerRadius, y: strokeEllipseRect.minY))
                context.addArc(tangent1End: CGPoint(x: strokeEllipseRect.maxX, y: strokeEllipseRect.minY), tangent2End: CGPoint(x: strokeEllipseRect.maxX, y: strokeEllipseRect.minY + smallerRadius), radius: smallerRadius)
                context.addLine(to: CGPoint(x: strokeEllipseRect.maxX, y: strokeEllipseRect.maxY - radius))
                context.addArc(tangent1End: CGPoint(x: strokeEllipseRect.maxX, y: strokeEllipseRect.maxY), tangent2End: CGPoint(x: strokeEllipseRect.maxX - radius, y: strokeEllipseRect.maxY), radius: radius)
                context.addLine(to: CGPoint(x: strokeEllipseRect.minX + smallerRadius, y: strokeEllipseRect.maxY))
                context.addArc(tangent1End: CGPoint(x: strokeEllipseRect.minX, y: strokeEllipseRect.maxY), tangent2End: CGPoint(x: strokeEllipseRect.minX, y: strokeEllipseRect.maxY - smallerRadius), radius: smallerRadius)
                context.closePath()
                context.strokePath()

                context.resetClip()
                context.setBlendMode(.normal)
            }
            innerImage.draw(in: CGRect(origin: .zero, size: size))
        }.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5))
    }

    // MARK: - Foreground Image (with inner shadows and gradient border)

    static func generateForegroundImage(
        size: CGSize,
        isDark: Bool,
        fillColor: UIColor
    ) -> UIImage {
        var size = size
        if size == .zero {
            size = CGSize(width: 1.0, height: 1.0)
        }

        return UIGraphicsImageRenderer(size: size).image { ctx in
            let context = ctx.cgContext
            context.clear(CGRect(origin: .zero, size: size))

            let maxColor = UIColor(white: 1.0, alpha: isDark ? 0.2 : 0.9)
            let minColor = UIColor(white: 1.0, alpha: 0.0)

            context.setFillColor(fillColor.cgColor)
            context.fillEllipse(in: CGRect(origin: .zero, size: size))

            let lineWidth: CGFloat = isDark ? 0.33 : 0.66

            context.saveGState()

            // Inner shadows
            let darkShadeColor = UIColor(white: isDark ? 1.0 : 0.0, alpha: isDark ? 0.0 : 0.035)
            let lightShadeColor = UIColor(white: isDark ? 0.0 : 1.0, alpha: isDark ? 0.0 : 0.035)
            let innerShadowBlur: CGFloat = 24.0

            context.resetClip()
            context.addEllipse(in: CGRect(origin: .zero, size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
            context.clip()
            context.addRect(CGRect(origin: .zero, size: size).insetBy(dx: -100.0, dy: -100.0))
            context.addEllipse(in: CGRect(origin: .zero, size: size))
            context.setFillColor(UIColor.black.cgColor)
            context.setShadow(offset: CGSize(width: 10.0, height: -10.0), blur: innerShadowBlur, color: darkShadeColor.cgColor)
            context.fillPath(using: .evenOdd)

            context.resetClip()
            context.addEllipse(in: CGRect(origin: .zero, size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
            context.clip()
            context.addRect(CGRect(origin: .zero, size: size).insetBy(dx: -100.0, dy: -100.0))
            context.addEllipse(in: CGRect(origin: .zero, size: size))
            context.setFillColor(UIColor.black.cgColor)
            context.setShadow(offset: CGSize(width: -10.0, height: 10.0), blur: innerShadowBlur, color: lightShadeColor.cgColor)
            context.fillPath(using: .evenOdd)

            context.restoreGState()

            context.setLineWidth(lineWidth)

            // Left half gradient border
            context.addRect(CGRect(x: 0.0, y: 0.0, width: size.width * 0.5, height: size.height))
            context.clip()
            context.addEllipse(in: CGRect(origin: .zero, size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
            context.replacePathWithStrokedPath()
            context.clip()

            do {
                var locations: [CGFloat] = [0.0, 0.5, 0.5 + 0.2, 1.0 - 0.1, 1.0]
                let colors: [CGColor] = [maxColor.cgColor, maxColor.cgColor, minColor.cgColor, minColor.cgColor, maxColor.cgColor]

                let colorSpace = CGColorSpaceCreateDeviceRGB()
                let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!

                context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
            }

            // Right half gradient border
            context.resetClip()
            context.addRect(CGRect(x: size.width - size.width * 0.5, y: 0.0, width: size.width * 0.5, height: size.height))
            context.clip()
            context.addEllipse(in: CGRect(origin: .zero, size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5))
            context.replacePathWithStrokedPath()
            context.clip()

            do {
                var locations: [CGFloat] = [0.0, 0.1, 0.5 - 0.2, 0.5, 1.0]
                let colors: [CGColor] = [maxColor.cgColor, minColor.cgColor, minColor.cgColor, maxColor.cgColor, maxColor.cgColor]

                let colorSpace = CGColorSpaceCreateDeviceRGB()
                let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!

                context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
            }
        }.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5))
    }

    // MARK: - Shadow Image

    static func generateShadowImage(cornerRadius: CGFloat) -> UIImage? {
        let inset: CGFloat = 32
        let innerInset: CGFloat = 0.5
        let diameter = cornerRadius * 2
        let totalSize = CGSize(width: inset * 2 + diameter, height: inset * 2 + diameter)

        let image = UIGraphicsImageRenderer(size: totalSize).image { ctx in
            let context = ctx.cgContext
            context.clear(CGRect(origin: .zero, size: totalSize))

            context.setFillColor(UIColor.black.cgColor)
            context.setShadow(
                offset: CGSize(width: 0, height: 1),
                blur: 40.0,
                color: UIColor(white: 0.0, alpha: 0.04).cgColor
            )
            let ellipseRect = CGRect(
                x: inset + innerInset,
                y: inset + innerInset,
                width: totalSize.width - (inset + innerInset) * 2,
                height: totalSize.height - (inset + innerInset) * 2
            )
            context.fillEllipse(in: ellipseRect)

            context.setFillColor(UIColor.clear.cgColor)
            context.setBlendMode(.copy)
            context.fillEllipse(in: ellipseRect)
        }
        return image.stretchableImage(
            withLeftCapWidth: Int(inset + cornerRadius),
            topCapHeight: Int(inset + cornerRadius)
        )
    }
}
