//
//  CLSystemTokens.swift

//
//  Created by Leon on 2025/7/11.
//

import UIKit

// MARK: - Convenience functions
extension UIColor {
    static var c: SystemColor {
        SystemColor()
    }
}

extension UIFont {
    static var t: SystemFont {
        SystemFont()
    }
}

@dynamicMemberLookup
struct SystemColor {
    subscript(dynamicMember key: String) -> UIColor {
        guard let token = CLEnumSystemToken.allCases.first(where: { "\($0)" == key }) else {
            return .black
        }
        return CLSystemToken.color(token: token) ?? .red
    }

    func systemTokenValue(for key: String) -> String? {
        CLEnumSystemToken.allCases.first(where: { "\($0)" == key })?.rawValue
    }
}

@dynamicMemberLookup
struct SystemFont {
    subscript(dynamicMember key: String) -> UIFont {
        guard let token = CLEnumSystemToken.allCases.first(where: { "\($0)" == key }) else {
            return .systemFont(ofSize: 14)
        }
        return CLSystemToken.font(token: token)
    }

    func systemTokenValue(for key: String) -> String? {
        CLEnumSystemToken.allCases.first(where: { "\($0)" == key })?.rawValue
    }
}



// MARK: - CLEnumSystemToken: Enums and Constants

enum CLEnumSystemToken: String, CaseIterable {
    init?(caseName: String) {
        self = CLEnumSystemToken.allCases.first(where: { "\($0)" == caseName }) ?? .cpn
    }

    case cpn = "color.primary.normal"
    case cph = "color.primary.hover"
    case cpp = "color.primary.press"
    case cpd = "color.primary.disabled"
    case cpvn = "color.primary.variant.normal"
    case cpvh = "color.primary.variant.hover"
    case cpvp = "color.primary.variant.press"
    case cpvd = "color.primary.variant.disabled"
    case cpgn = "color.primary.gradient.normal"
    case cpgh = "color.primary.gradient.hover"
    case cpgp = "color.primary.gradient.press"
    case cpgd = "color.primary.gradient.disabled"
    case cin = "color.important.normal"
    case cih = "color.important.hover"
    case cip = "color.important.press"
    case cid = "color.important.disabled"
    case civn = "color.important.variant.normal"
    case civh = "color.important.variant.hover"
    case civp = "color.important.variant.press"
    case civd = "color.important.variant.disabled"
    case cign = "color.important.gradient.normal"
    case cigh = "color.important.gradient.hover"
    case cigp = "color.important.gradient.press"
    case ciopn = "color.important.onpic.normal"
    case cposn = "color.positive.normal"
    case cposh = "color.positive.hover"
    case cposp = "color.positive.press"
    case cposd = "color.positive.disabled"
    case cposvn = "color.positive.variant.normal"
    case cposvh = "color.positive.variant.hover"
    case cposvp = "color.positive.variant.press"
    case cposvd = "color.positive.variant.disabled"
    case cposgn = "color.positive.gradient.normal"
    case cposopn = "color.positive.onpic.normal"
    case cwn = "color.warning.normal"
    case cwh = "color.warning.hover"
    case cwp = "color.warning.press"
    case cwd = "color.warning.disabled"
    case cwvn = "color.warning.variant.normal"
    case cwvh = "color.warning.variant.hover"
    case cwvp = "color.warning.variant.press"
    case cwvd = "color.warning.variant.disabled"
    case cwgn = "color.warning.gradient.normal"
    case cwopn = "color.warning.onpic.normal"
    case cen = "color.emphasis.normal"
    case ceh = "color.emphasis.hover"
    case cep = "color.emphasis.press"
    case ced = "color.emphasis.disabled"
    case cevn = "color.emphasis.variant.normal"
    case cevh = "color.emphasis.variant.hover"
    case cevp = "color.emphasis.variant.press"
    case cevd = "color.emphasis.variant.disabled"
    case cegn = "color.emphasis.grandient.normal"
    case cegh = "color.emphasis.grandient.hover"
    case cegp = "color.emphasis.grandient.press"
    case cegd = "color.emphasis.grandient.disabled"
    case ceopn = "color.emphasis.onpic.normal"
    case cbd = "color.background.default"
    case cbs = "color.background.specialmap"
    case cbdi = "color.background.district"
    case csbn = "color.surface.base.normal"
    case csbh = "color.surface.base.hover"
    case csbp = "color.surface.base.press"
    case csbsn = "color.surface.base.specialmap.normal"
    case csbsh = "color.surface.base.specialmap.hover"
    case csbsp = "color.surface.base.specialmap.press"
    case csbsd = "color.surface.base.specialmap.disabled"
    case csfn = "color.surface.float.normal"
    case csfh = "color.surface.float.hover"
    case csfp = "color.surface.float.press"
    case cstn = "color.surface.top.normal"
    case csth = "color.surface.top.hover"
    case cstp = "color.surface.top.press"
    case cstd = "color.surface.top.disabled"
    case csdn = "color.surface.district.normal"
    case csdh = "color.surface.district.hover"
    case csdp = "color.surface.district.press"
    case csdd = "color.surface.district.disabled"
    case csnn = "color.surface.nest.normal"
    case csnh = "color.surface.nest.hover"
    case csnp = "color.surface.nest.press"
    case csnd = "color.surface.nest.disabled"
    case csen = "color.surface.element.normal"
    case cseh = "color.surface.element.hover"
    case csep = "color.surface.element.press"
    case csed = "color.surface.element.disabled"
    case csedn = "color.surface.element.dark.normal"
    case csedh = "color.surface.element.dark.hover"
    case csedp = "color.surface.element.dark.press"
    case csedd = "color.surface.element.dark.disabled"
    case cseln = "color.surface.element.light.normal"
    case cselh = "color.surface.element.light.hover"
    case cselp = "color.surface.element.light.press"
    case cseld = "color.surface.element.light.disabled"
    case cswn = "color.surface.white.normal"
    case cswh = "color.surface.white.hover"
    case cswp = "color.surface.white.press"
    case cswd = "color.surface.white.disabled"
    case csbn2 = "color.surface.black.normal"
    case con = "color.outline.normal"
    case coh = "color.outline.hover"
    case cop = "color.outline.press"
    case cod = "color.outline.disabled"
    case copr = "color.overlay.primary"
    case cogr = "color.overlay.gradient"
    case codr = "color.overlay.dark"
    /// graident
    case cob = "color.overlay.background"
    case cobase = "color.overlay.base"
    /// vip gradient
    case ccvn = "color.context.vip.normal"
    case ctpn = "color.txt.primary.normal"
    case ctph = "color.txt.primary.hover"
    case ctpp = "color.txt.primary.press"
    case ctpd = "color.txt.primary.disabled"
    case ctpsn = "color.txt.primary.specialmap.normal"
    case ctpsh = "color.txt.primary.specialmap.hover"
    case ctpsp = "color.txt.primary.specialmap.press"
    case ctpsd = "color.txt.primary.specialmap.disabled"
    case ctsn = "color.txt.secondary.normal"
    case ctsh = "color.txt.secondary.hover"
    case ctsp = "color.txt.secondary.press"
    case ctsd = "color.txt.secondary.disabled"
    case cttn = "color.txt.tertiary.normal"
    case ctg = "color.txt.grass"
    case ctd = "color.txt.disabled"
    case td = "txt.display"
    case tdxl = "txt.display.xl"
    case tdl = "txt.display.l"
    case tdm = "txt.display.m"
    case tds = "txt.display.s"
    case thl = "txt.headline.l"
    case thm = "txt.headline.m"
    case ths = "txt.headline.s"
    case ttl = "txt.title.l"
    case ttm = "txt.title.m"
    case tts = "txt.title.s"
    case tbsl = "txt.bodySemibold.l"
    case tbsm = "txt.bodySemibold.m"
    case tbss = "txt.bodySemibold.s"
    case tbl = "txt.body.l"
    case tbm = "txt.body.m"
    case tbim = "txt.body.italic.m"
    case tbs = "txt.body.s"
    case tll = "txt.label.l"
    case tlm = "txt.label.m"
    case tls = "txt.label.s"
    case tndxl = "txt.numDisplay.xl"
    case tndl = "txt.numDisplay.l"
    case tndm = "txt.numDisplay.m"
    case tnds = "txt.numDisplay.s"
    case tnmxl = "txt.numMonotype.xl"
    case tnml = "txt.numMonotype.l"
    case tnmm = "txt.numMonotype.m"
    case tnms = "txt.numMonotype.s"
    case tnmxs = "txt.numMonotype.xs"
    case shs = "shadow.s"
    case shm = "shadow.m"
    case shl = "shadow.l"
    case rxs = "radius.xs"
    case rs = "radius.s"
    case rm = "radius.m"
    case rl = "radius.l"
    case rxl = "radius.xl"
    case rxxl = "radius.xxl"
    case rr = "radius.round"
    case rp = "radius.pill"
    case bd = "border.divider"
    case bs = "border.s"
    case bm = "border.m"
    case bl = "border.l"
}

func KeyForSystemTokenFrom(_ token: CLEnumSystemToken) -> String {
    return token.rawValue
}

// MARK: - EPShadow

struct EPShadow {
    var color: UIColor?
    var opacity: Float = 0
    var offset: CGSize = .zero
    var radius: CGFloat = 0
}

// MARK: - EPGradient

struct EPGradient {
    var direction: CGPoint = .zero
    var firstColor: UIColor?
    var secondColor: UIColor?
    var thirdColor : UIColor?

    public func toImage(size: CGSize) -> UIImage? {
        guard let firstColor = firstColor?.cgColor, let secondColor = secondColor?.cgColor else {
            return nil
        }

        // 根据 direction 计算 startPoint 和 endPoint
        let startPoint: CGPoint
        let endPoint: CGPoint

        switch direction {
        case CGPoint(x: 0, y: -1): // 从上到下
            startPoint = CGPoint(x: 0.5, y: 0)
            endPoint = CGPoint(x: 0.5, y: 1)
        case CGPoint(x: 1, y: -1): // 从左上到右下
            startPoint = CGPoint(x: 0, y: 0)
            endPoint = CGPoint(x: 1, y: 1)
        default:
            // 如果 direction 不匹配已知方向，返回 nil 或使用默认方向
            startPoint = CGPoint(x: 0, y: 0.5)
            endPoint = CGPoint(x: 1, y: 0.5) // 默认水平渐变
        }
        
        var colors = [firstColor, secondColor]
        if(colors.count == 3){
            if let third = thirdColor?.cgColor{
                colors = [firstColor, secondColor, third]
            }
        }

        // 调用 gradientImageWithSize 生成图像
        return UIImage.gradientImageWithSize(
            size: size,
            colors: colors,
            startPoint: startPoint,
            endPoint: endPoint
        )
    }
    
    public func colors() -> [UIColor]{
        var colors = [UIColor]()
        if let first = firstColor{
            colors.append(first)
        }
        if let second = secondColor{
            colors.append(second)
        }
        if let third = thirdColor{
            colors.append(third)
        }
        return colors
    }
}

// MARK: - EPTypography

struct EPTypography {
    var font: UIFont?
    var lineHeight: CGFloat = 0
}

// MARK: - EPBaseObject

class EPBaseObject {
    static func subGetColorByTokenValues(_ values: [String]) -> UIColor? {
        guard !values.isEmpty else {
            preconditionFailure("Invalid color values: empty array")
            return nil
        }

        guard let hex = values.first, hex.hasPrefix("#") else {
            print("❌ Invalid color format for values: \(values)")
            return nil
        }

        let hexString = hex.replacingOccurrences(of: "#", with: "")
        var rgb: UInt64 = 0
        Scanner(string: hexString).scanHexInt64(&rgb)

        let red = CGFloat((rgb >> 16) & 0xFF) / 255.0
        let green = CGFloat((rgb >> 8) & 0xFF) / 255.0
        let blue = CGFloat(rgb & 0xFF) / 255.0
        let alpha: CGFloat = values.count > 1 ? CGFloat(Float(values.last!) ?? 1.0) : 1.0

        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    }
}

// MARK: - CLSystemToken

class CLSystemToken {
    static let baseTokens = CLBaseTokens.shared

    // MARK: - Color Methods

    static func color(token: CLEnumSystemToken) -> UIColor? {
        let key = KeyForSystemTokenFrom(token)
        let values = baseTokens.getTokensByKey(key)
        return EPBaseObject.subGetColorByTokenValues(values)
    }

    static func darkColor(token: CLEnumSystemToken) -> UIColor? {
        let key = KeyForSystemTokenFrom(token)
        let values = baseTokens.getDarkTokens(key)
        return EPBaseObject.subGetColorByTokenValues(values)
    }

    // MARK: - String Method

    static func string(token: CLEnumSystemToken) -> String? {
        let key = KeyForSystemTokenFrom(token)
        return baseTokens.getTokenByKey(key)
    }

    // MARK: - Float Method

    static func float(token: CLEnumSystemToken) -> CGFloat {
        let key = KeyForSystemTokenFrom(token)
        let value = baseTokens.getTokenByKey(key)
        guard let floatValue = Float(value) else {
            print("❌ Invalid float format for key: \(key)")
            return 0
        }
        return CGFloat(floatValue)
    }

    // MARK: - Radius Method

    static func radius(token: CLEnumSystemToken) -> CGFloat {
        let key = KeyForSystemTokenFrom(token)
        let value = baseTokens.getTokenByKey(key)
        guard let radius = Float(value) else {
            print("❌ Invalid radius format for key: \(key)")
            return 0
        }
        // print("🎨 radius: \(key) \(radius)") // DLog 替换为 print
        return CGFloat(radius)
    }

    // MARK: - Border Method

    static func border(token: CLEnumSystemToken) -> CGFloat {
        let key = KeyForSystemTokenFrom(token)
        let value = baseTokens.getTokenByKey(key)
        guard let border = Float(value) else {
            print("❌ Invalid border format for key: \(key)")
            return 0
        }
        // print("🎨 border: \(key) \(border)") // DLog 替换为 print
        return CGFloat(border)
    }

    // MARK: - Shadow Method

    static func shadow(token: CLEnumSystemToken) -> EPShadow {
        let key = KeyForSystemTokenFrom(token)
        let values = baseTokens.getTokensByKey(key)

        guard values.count == 4, values.first?.contains(EPConfigShadowPrefix) == true else {
            assertionFailure("Wrong format for shadow token: \(key)")
            return EPShadow()
        }

        var shadow = EPShadow()

        if let colorStr = values.first?.replacingOccurrences(of: EPConfigShadowPrefix, with: "") {
            let color = EPBaseObject.subGetColorByTokenValues([colorStr])
            shadow.color = color
        }

        if let opacity = Float(values[1]) {
            shadow.opacity = opacity
        }
        let offXy = values[2].components(separatedBy: "&")
        if offXy.count == 2,
           let x = Float(offXy[0]), let y = Float(offXy[1]) {
            shadow.offset = CGSize(width: CGFloat(x), height: CGFloat(y))
        }

        if let radius = Float(values[3]) {
            shadow.radius = CGFloat(radius)
        }

        return shadow
    }

    // MARK: - Gradient Method

    static func gradient(token: CLEnumSystemToken) -> EPGradient {
//        if token == .colorContextVipNormal {
//            assertionFailure("Special style rule, not used in this unified rule, please use another method")
//            return gradient(token: .colorPrimaryGradientNormal)
//        }

        let key = KeyForSystemTokenFrom(token)
        let values = baseTokens.getTokensByKey(key)

        if values.count <= 2  { // || values.first?.contains(EPConfigGradientPrefix) != true
            var gradient = EPGradient()
            gradient.direction = CGPoint(x: 1, y: 0)
            gradient.firstColor = EPBaseObject.subGetColorByTokenValues(values)
            gradient.secondColor = gradient.firstColor
            return gradient
        }

        // 方向+ 2个颜色，或者3个颜色
        guard values.count == 3 || values.count == 4 else {
            assertionFailure("Wrong format for gradient token: \(key)")
            return EPGradient()
        }

        var gradient = EPGradient()

        if let directionStr = values.first {
            switch directionStr {
            case "LTR":
                gradient.direction = CGPoint(x: 1, y: 0)
            case "TTB":
                gradient.direction = CGPoint(x: 0, y: 1)
            case "LTTRB":
                gradient.direction = CGPoint(x: 1, y: 1)
            default:
                assertionFailure("Not handled direction: \(directionStr)")
                gradient.direction = .zero
            }
        }

        let color1Str = values[1].replacingOccurrences(of: EPConfigGradientPrefix, with: "")
        let color1Array = color1Str.components(separatedBy: "&")
        gradient.firstColor = EPBaseObject.subGetColorByTokenValues(color1Array)

        let color2Str = values[2].replacingOccurrences(of: EPConfigGradientPrefix, with: "")
        let color2Array = color2Str.components(separatedBy: "&")
        gradient.secondColor = EPBaseObject.subGetColorByTokenValues(color2Array)
        
        if(values.count == 4){
            let color3Str = values[3].replacingOccurrences(of: EPConfigGradientPrefix, with: "")
            let color3Array = color3Str.components(separatedBy: "&")
            gradient.thirdColor = EPBaseObject.subGetColorByTokenValues(color3Array)
        }

        return gradient
    }

    // MARK: - Typography Method

    static func typography(token: CLEnumSystemToken) -> EPTypography {
        let key = KeyForSystemTokenFrom(token)
        let values = baseTokens.getTokensByKey(key)

        guard values.count == 4 else {
            assertionFailure("Wrong format for typography token: \(key)")
            return EPTypography()
        }

        var typography = EPTypography()

        var fontName = values[0]
        guard let fontSize = Int(values[1]),
              let weight = Int(values[2]),
              let lineHeight = Int(values[3]) else {
            assertionFailure("Invalid typography values for token: \(key)")
            return EPTypography()
        }

        if !fontName.contains("-Italic") {
            switch weight {
            case 400:
                fontName += "-Regular"
            case 500:
                fontName += "-Medium"
            case 600:
                fontName += "-SemiBold"
            case 700:
                fontName += "-Bold"
            default:
                assertionFailure("Not handled font weight: \(weight)")
            }
        }

        typography.font = UIFont(name: fontName, size: CGFloat(fontSize))
        typography.lineHeight = CGFloat(lineHeight)

        return typography
    }

    // MARK: - Font Method

    static func font(token: CLEnumSystemToken) -> UIFont {
        let key = KeyForSystemTokenFrom(token)
        let values = baseTokens.getTokensByKey(key)

        guard values.count == 4 else {
            assertionFailure("Wrong format for font token: \(key)")
            return UIFont.systemFont(ofSize: 14)
        }

        var fontName = values[0]
        guard let fontSize = Int(values[1]),
              let weight = Int(values[2]) else {
            assertionFailure("Invalid font values for token: \(key)")
            return UIFont.systemFont(ofSize: 14)
        }
        
        if(fontName .isNotBlank){
            fontName = fontName.removingAllWhitespace
        }

        if !fontName.contains("-Italic") {
            switch weight {
            case 400:
                fontName += "-Regular"
            case 500:
                fontName += "-Medium"
            case 600:
                fontName += "-SemiBold"
            case 700:
                fontName += "-Bold"
            default:
                assertionFailure("Not handled font weight: \(weight)")
            }
        }

        return UIFont(name: fontName, size: CGFloat(fontSize)) ?? UIFont.systemFont(ofSize: 14)
    }
}
