import SwiftUI

#if os(macOS)
import AppKit
private typealias PlatformColor = NSColor
#else
import UIKit
private typealias PlatformColor = UIColor
#endif

struct EditorTheme {
    let text: Color
    let background: Color
    let cursor: Color
    let selection: Color
    let syntax: SyntaxColors
    let boldKeywords: Bool
    let italicComments: Bool
    let underlineLinks: Bool
    let boldMarkdownHeadings: Bool
}

private struct ThemePalette {
    let text: Color
    let background: Color
    let cursor: Color
    let selection: Color
    let keyword: Color
    let string: Color
    let number: Color
    let comment: Color
    let type: Color
    let property: Color
    let builtin: Color
}

struct ThemePaletteColors {
    let text: Color
    let background: Color
    let cursor: Color
    let selection: Color
    let keyword: Color
    let string: Color
    let number: Color
    let comment: Color
    let type: Color
    let property: Color
    let builtin: Color
}

private struct RGBColorComponents {
    let red: Double
    let green: Double
    let blue: Double
    let alpha: Double
}

private func colorComponents(_ color: Color) -> RGBColorComponents? {
#if os(macOS)
    let platform = PlatformColor(color)
    guard let srgb = platform.usingColorSpace(.sRGB) else { return nil }
    return RGBColorComponents(
        red: Double(srgb.redComponent),
        green: Double(srgb.greenComponent),
        blue: Double(srgb.blueComponent),
        alpha: Double(srgb.alphaComponent)
    )
#else
    let platform = PlatformColor(color)
    var red: CGFloat = 0
    var green: CGFloat = 0
    var blue: CGFloat = 0
    var alpha: CGFloat = 0
    guard platform.getRed(&red, green: &green, blue: &blue, alpha: &alpha) else { return nil }
    return RGBColorComponents(red: Double(red), green: Double(green), blue: Double(blue), alpha: Double(alpha))
#endif
}

private func relativeLuminance(_ components: RGBColorComponents) -> Double {
    (0.2126 * components.red) + (0.7152 * components.green) + (0.0722 * components.blue)
}

private func blend(_ source: Color, with target: Color, amount: Double) -> Color {
    guard let sourceComponents = colorComponents(source), let targetComponents = colorComponents(target) else {
        return source
    }
    let clamped = min(1.0, max(0.0, amount))
    return Color(
        red: sourceComponents.red + ((targetComponents.red - sourceComponents.red) * clamped),
        green: sourceComponents.green + ((targetComponents.green - sourceComponents.green) * clamped),
        blue: sourceComponents.blue + ((targetComponents.blue - sourceComponents.blue) * clamped),
        opacity: sourceComponents.alpha + ((targetComponents.alpha - sourceComponents.alpha) * clamped)
    )
}

private func modeAdjustedEditorBackground(_ background: Color, colorScheme: ColorScheme) -> Color {
    guard let components = colorComponents(background) else { return background }
    let luminance = relativeLuminance(components)

    if colorScheme == .light {
        // Keep all themes readable in light/system-light by lifting dark palettes.
        if luminance >= 0.78 { return background }
        let targetLuminance = 0.96
        let normalized = (targetLuminance - luminance) / max(0.0001, 1.0 - luminance)
        let mixAmount = min(0.95, max(0.70, normalized))
        return blend(background, with: .white, amount: mixAmount)
    }

    // Keep all themes readable in dark/system-dark by lowering bright palettes.
    if luminance <= 0.28 { return background }
    let targetLuminance = 0.14
    let normalized = (luminance - targetLuminance) / max(0.0001, luminance)
    let mixAmount = min(0.90, max(0.55, normalized))
    return blend(background, with: .black, amount: mixAmount)
}

private func modeAdjustedSyntaxColor(
    _ color: Color,
    colorScheme: ColorScheme,
    darkenInLight amountLight: Double,
    brightenInDark amountDark: Double
) -> Color {
    if colorScheme == .light {
        return blend(color, with: .black, amount: amountLight)
    }
    return blend(color, with: .white, amount: amountDark)
}

private func emphasizedSelectionColor(for canonicalName: String, fallback: Color) -> Color {
    switch canonicalName {
    case "Neon Glow":
        return Color(red: 0.95, green: 0.17, blue: 0.56)
    case "Neon Flow":
        return Color(red: 0.95, green: 0.17, blue: 0.56)
    case "Neon Voltage":
        return Color(red: 0.00, green: 0.72, blue: 1.00)
    case "Laserwave":
        return Color(red: 0.98, green: 0.24, blue: 0.88)
    case "Cyber Lime":
        return Color(red: 0.45, green: 0.78, blue: 0.16)
    case "Plasma Storm":
        return Color(red: 0.58, green: 0.46, blue: 1.00)
    case "Inferno Neon":
        return Color(red: 1.00, green: 0.38, blue: 0.16)
    case "Ultraviolet Flux":
        return Color(red: 0.84, green: 0.36, blue: 1.00)
    case "Prism Daylight":
        return Color(red: 0.24, green: 0.50, blue: 0.96)
    case "Dracula":
        return Color(red: 1.00, green: 0.48, blue: 0.78)
    case "One Dark Pro":
        return Color(red: 0.35, green: 0.67, blue: 0.98)
    case "Nord":
        return Color(red: 0.56, green: 0.74, blue: 0.73)
    case "Tokyo Night":
        return Color(red: 0.48, green: 0.64, blue: 0.97)
    case "Gruvbox":
        return Color(red: 1.00, green: 0.50, blue: 0.10)
    case "Arc":
        return Color(red: 0.35, green: 0.67, blue: 0.98)
    case "Dusk":
        return Color(red: 0.93, green: 0.54, blue: 0.94)
    case "Aurora":
        return Color(red: 0.35, green: 0.96, blue: 0.76)
    case "Horizon":
        return Color(red: 0.99, green: 0.46, blue: 0.36)
    case "Midnight":
        return Color(red: 0.25, green: 0.78, blue: 0.98)
    case "Mono":
        return Color(red: 0.82, green: 0.82, blue: 0.82)
    case "Paper":
        return Color(red: 0.24, green: 0.50, blue: 0.96)
    case "Solar":
        return Color(red: 0.99, green: 0.64, blue: 0.24)
    case "Pulse":
        return Color(red: 0.98, green: 0.54, blue: 0.62)
    case "Mocha":
        return Color(red: 0.82, green: 0.60, blue: 0.98)
    case "Custom":
        return fallback
    default:
        return fallback
    }
}

///MARK: Syntax Adjustment Profiles

// Internal strategy for token color adjustment per theme family.
private enum SyntaxAdjustmentProfile {
    case standard
    case neonRaw
}

private func adjustedSyntaxColor(
    _ color: Color,
    colorScheme: ColorScheme,
    profile: SyntaxAdjustmentProfile,
    darkenInLight amountLight: Double,
    brightenInDark amountDark: Double
) -> Color {
    switch profile {
    case .standard:
        return modeAdjustedSyntaxColor(
            color,
            colorScheme: colorScheme,
            darkenInLight: amountLight,
            brightenInDark: amountDark
        )
    case .neonRaw:
        // Keep Neon Glow vivid in light mode and only apply a tiny dark-mode lift.
        if colorScheme == .light { return color }
        return blend(color, with: .white, amount: 0.03)
    }
}

///MARK: Theme Name Canonicalization

// Canonical theme names shown in settings and used for palette lookup.
let editorThemeNames: [String] = [
    "Neon Glow",
    "Neon Flow",
    "Neon Voltage",
    "Laserwave",
    "Cyber Lime",
    "Plasma Storm",
    "Inferno Neon",
    "Ultraviolet Flux",
    "Prism Daylight",
    "Custom",
    "Dracula",
    "One Dark Pro",
    "Nord",
    "Tokyo Night",
    "Gruvbox",
    "Arc",
    "Dusk",
    "Aurora",
    "Horizon",
    "Midnight",
    "Mono",
    "Paper",
    "Solar",
    "Pulse",
    "Mocha"
]

// Normalize persisted theme values so legacy/case variants still resolve correctly.
func canonicalThemeName(_ rawName: String) -> String {
    let trimmed = rawName.trimmingCharacters(in: .whitespacesAndNewlines)
    guard !trimmed.isEmpty else { return "Neon Glow" }

    if let exact = editorThemeNames.first(where: { $0 == trimmed }) {
        return exact
    }

    if let caseInsensitive = editorThemeNames.first(where: { $0.caseInsensitiveCompare(trimmed) == .orderedSame }) {
        return caseInsensitive
    }

    return "Neon Glow"
}

private func paletteForThemeName(_ name: String, defaults: UserDefaults) -> ThemePalette {
    let canonicalName = canonicalThemeName(name)
    let palette: ThemePalette = {
        switch canonicalName {
        case "Neon Glow":
            return ThemePalette(
                text: Color(red: 0.94, green: 0.94, blue: 0.94),
                background: Color(red: 0.06, green: 0.07, blue: 0.09),
                cursor: Color(red: 0.30, green: 0.60, blue: 0.90),
                selection: Color(red: 0.18, green: 0.24, blue: 0.33),
                keyword: Color(red: 0.93, green: 0.00, blue: 0.88),
                string: Color(red: 0.00, green: 0.243137, blue: 1.00),
                number: Color(red: 1.00, green: 0.02, blue: 0.12),
                comment: Color(red: 0.38, green: 0.38, blue: 0.40),
                type: Color(red: 0.20, green: 0.82, blue: 0.41),
                property: Color(red: 0.86, green: 0.44, blue: 0.84),
                builtin: Color(red: 0.92, green: 0.47, blue: 0.53)
            )
        case "Neon Flow":
            return ThemePalette(
                text: Color(red: 0.00, green: 0.00, blue: 0.00),
                background: Color(red: 1.00, green: 1.00, blue: 1.00),
                cursor: Color(red: 0.29, green: 0.60, blue: 0.91),
                selection: Color(red: 0.95, green: 0.17, blue: 0.56),
                keyword: Color(red: 0.92, green: 0.05, blue: 0.91),
                string: Color(red: 0.09, green: 0.56, blue: 0.91),
                number: Color(red: 0.95, green: 0.17, blue: 0.56),
                comment: Color(red: 0.62, green: 0.62, blue: 0.62),
                type: Color(red: 0.08, green: 0.89, blue: 0.37),
                property: Color(red: 0.92, green: 0.05, blue: 0.91),
                builtin: Color(red: 0.96, green: 0.20, blue: 0.04)
            )
        case "Neon Voltage":
            return ThemePalette(
                text: Color(red: 0.95, green: 0.98, blue: 1.00),
                background: Color(red: 0.04, green: 0.06, blue: 0.09),
                cursor: Color(red: 0.00, green: 0.92, blue: 1.00),
                selection: Color(red: 0.12, green: 0.20, blue: 0.30),
                keyword: Color(red: 0.00, green: 0.92, blue: 1.00),
                string: Color(red: 0.35, green: 1.00, blue: 0.72),
                number: Color(red: 1.00, green: 0.72, blue: 0.10),
                comment: Color(red: 0.48, green: 0.56, blue: 0.68),
                type: Color(red: 0.74, green: 0.52, blue: 1.00),
                property: Color(red: 0.32, green: 0.88, blue: 1.00),
                builtin: Color(red: 1.00, green: 0.34, blue: 0.74)
            )
        case "Laserwave":
            return ThemePalette(
                text: Color(red: 0.96, green: 0.93, blue: 1.00),
                background: Color(red: 0.10, green: 0.05, blue: 0.14),
                cursor: Color(red: 0.98, green: 0.24, blue: 0.88),
                selection: Color(red: 0.25, green: 0.15, blue: 0.34),
                keyword: Color(red: 1.00, green: 0.28, blue: 0.86),
                string: Color(red: 0.22, green: 0.94, blue: 1.00),
                number: Color(red: 1.00, green: 0.60, blue: 0.30),
                comment: Color(red: 0.60, green: 0.52, blue: 0.72),
                type: Color(red: 0.65, green: 0.72, blue: 1.00),
                property: Color(red: 0.36, green: 0.98, blue: 0.84),
                builtin: Color(red: 1.00, green: 0.42, blue: 0.56)
            )
        case "Cyber Lime":
            return ThemePalette(
                text: Color(red: 0.94, green: 0.98, blue: 0.92),
                background: Color(red: 0.07, green: 0.09, blue: 0.06),
                cursor: Color(red: 0.18, green: 0.48, blue: 0.96),
                selection: Color(red: 0.18, green: 0.25, blue: 0.14),
                keyword: Color(red: 0.45, green: 0.78, blue: 0.16),
                string: Color(red: 0.13, green: 0.70, blue: 0.50),
                number: Color(red: 1.00, green: 0.86, blue: 0.22),
                comment: Color(red: 0.54, green: 0.62, blue: 0.50),
                type: Color(red: 0.48, green: 0.86, blue: 1.00),
                property: Color(red: 0.56, green: 0.78, blue: 0.28),
                builtin: Color(red: 1.00, green: 0.48, blue: 0.40)
            )
        case "Plasma Storm":
            return ThemePalette(
                text: Color(red: 0.95, green: 0.95, blue: 1.00),
                background: Color(red: 0.06, green: 0.05, blue: 0.10),
                cursor: Color(red: 0.54, green: 0.44, blue: 1.00),
                selection: Color(red: 0.16, green: 0.12, blue: 0.30),
                keyword: Color(red: 0.58, green: 0.46, blue: 1.00),
                string: Color(red: 0.24, green: 0.90, blue: 1.00),
                number: Color(red: 1.00, green: 0.52, blue: 0.20),
                comment: Color(red: 0.54, green: 0.50, blue: 0.68),
                type: Color(red: 0.80, green: 0.54, blue: 1.00),
                property: Color(red: 0.66, green: 0.78, blue: 1.00),
                builtin: Color(red: 1.00, green: 0.30, blue: 0.66)
            )
        case "Inferno Neon":
            return ThemePalette(
                text: Color(red: 1.00, green: 0.94, blue: 0.90),
                background: Color(red: 0.11, green: 0.05, blue: 0.03),
                cursor: Color(red: 1.00, green: 0.46, blue: 0.08),
                selection: Color(red: 0.28, green: 0.12, blue: 0.08),
                keyword: Color(red: 1.00, green: 0.38, blue: 0.16),
                string: Color(red: 1.00, green: 0.74, blue: 0.24),
                number: Color(red: 1.00, green: 0.26, blue: 0.48),
                comment: Color(red: 0.70, green: 0.54, blue: 0.46),
                type: Color(red: 1.00, green: 0.56, blue: 0.20),
                property: Color(red: 1.00, green: 0.70, blue: 0.32),
                builtin: Color(red: 1.00, green: 0.18, blue: 0.30)
            )
        case "Ultraviolet Flux":
            return ThemePalette(
                text: Color(red: 0.96, green: 0.93, blue: 1.00),
                background: Color(red: 0.08, green: 0.04, blue: 0.12),
                cursor: Color(red: 0.84, green: 0.36, blue: 1.00),
                selection: Color(red: 0.21, green: 0.12, blue: 0.30),
                keyword: Color(red: 0.84, green: 0.36, blue: 1.00),
                string: Color(red: 0.32, green: 0.98, blue: 0.96),
                number: Color(red: 1.00, green: 0.62, blue: 0.24),
                comment: Color(red: 0.60, green: 0.52, blue: 0.72),
                type: Color(red: 1.00, green: 0.38, blue: 0.86),
                property: Color(red: 0.68, green: 0.72, blue: 1.00),
                builtin: Color(red: 1.00, green: 0.28, blue: 0.56)
            )
        case "Prism Daylight":
            return ThemePalette(
                text: Color(red: 0.08, green: 0.10, blue: 0.14),
                background: Color(red: 0.96, green: 0.97, blue: 0.99),
                cursor: Color(red: 0.14, green: 0.34, blue: 0.84),
                selection: Color(red: 0.84, green: 0.89, blue: 0.99),
                keyword: Color(red: 0.48, green: 0.13, blue: 0.71),
                string: Color(red: 0.05, green: 0.48, blue: 0.33),
                number: Color(red: 0.71, green: 0.28, blue: 0.03),
                comment: Color(red: 0.37, green: 0.42, blue: 0.49),
                type: Color(red: 0.00, green: 0.34, blue: 0.76),
                property: Color(red: 0.64, green: 0.11, blue: 0.67),
                builtin: Color(red: 0.76, green: 0.25, blue: 0.05)
            )
        case "Dracula":
            return ThemePalette(
                text: Color(red: 0.97, green: 0.97, blue: 0.95),
                background: Color(red: 0.16, green: 0.17, blue: 0.21),
                cursor: Color(red: 0.97, green: 0.97, blue: 0.95),
                selection: Color(red: 0.27, green: 0.28, blue: 0.35),
                keyword: Color(red: 1.00, green: 0.48, blue: 0.78),
                string: Color(red: 0.95, green: 0.98, blue: 0.55),
                number: Color(red: 0.74, green: 0.58, blue: 0.98),
                comment: Color(red: 0.38, green: 0.45, blue: 0.64),
                type: Color(red: 0.55, green: 0.91, blue: 0.99),
                property: Color(red: 0.31, green: 0.98, blue: 0.48),
                builtin: Color(red: 1.00, green: 0.72, blue: 0.42)
            )
        case "One Dark Pro":
            return ThemePalette(
                text: Color(red: 0.67, green: 0.70, blue: 0.75),
                background: Color(red: 0.16, green: 0.17, blue: 0.20),
                cursor: Color(red: 0.32, green: 0.55, blue: 1.00),
                selection: Color(red: 0.24, green: 0.27, blue: 0.32),
                keyword: Color(red: 0.78, green: 0.47, blue: 0.87),
                string: Color(red: 0.60, green: 0.76, blue: 0.47),
                number: Color(red: 0.82, green: 0.60, blue: 0.40),
                comment: Color(red: 0.36, green: 0.39, blue: 0.44),
                type: Color(red: 0.90, green: 0.75, blue: 0.48),
                property: Color(red: 0.38, green: 0.69, blue: 0.94),
                builtin: Color(red: 0.34, green: 0.71, blue: 0.76)
            )
        case "Nord":
            return ThemePalette(
                text: Color(red: 0.85, green: 0.87, blue: 0.91),
                background: Color(red: 0.18, green: 0.20, blue: 0.25),
                cursor: Color(red: 0.53, green: 0.75, blue: 0.82),
                selection: Color(red: 0.26, green: 0.30, blue: 0.37),
                keyword: Color(red: 0.51, green: 0.63, blue: 0.76),
                string: Color(red: 0.64, green: 0.75, blue: 0.55),
                number: Color(red: 0.71, green: 0.56, blue: 0.68),
                comment: Color(red: 0.38, green: 0.43, blue: 0.53),
                type: Color(red: 0.56, green: 0.74, blue: 0.73),
                property: Color(red: 0.82, green: 0.53, blue: 0.44),
                builtin: Color(red: 0.92, green: 0.80, blue: 0.55)
            )
        case "Tokyo Night":
            return ThemePalette(
                text: Color(red: 0.75, green: 0.79, blue: 0.96),
                background: Color(red: 0.10, green: 0.11, blue: 0.15),
                cursor: Color(red: 0.48, green: 0.64, blue: 0.97),
                selection: Color(red: 0.20, green: 0.28, blue: 0.49),
                keyword: Color(red: 0.73, green: 0.60, blue: 0.97),
                string: Color(red: 0.62, green: 0.81, blue: 0.42),
                number: Color(red: 1.00, green: 0.62, blue: 0.39),
                comment: Color(red: 0.34, green: 0.37, blue: 0.54),
                type: Color(red: 0.49, green: 0.81, blue: 1.00),
                property: Color(red: 0.16, green: 0.77, blue: 0.87),
                builtin: Color(red: 0.97, green: 0.46, blue: 0.56)
            )
        case "Gruvbox":
            return ThemePalette(
                text: Color(red: 0.92, green: 0.86, blue: 0.70),
                background: Color(red: 0.16, green: 0.16, blue: 0.16),
                cursor: Color(red: 0.98, green: 0.74, blue: 0.18),
                selection: Color(red: 0.31, green: 0.29, blue: 0.27),
                keyword: Color(red: 0.98, green: 0.29, blue: 0.20),
                string: Color(red: 0.72, green: 0.73, blue: 0.15),
                number: Color(red: 0.83, green: 0.53, blue: 0.61),
                comment: Color(red: 0.57, green: 0.51, blue: 0.46),
                type: Color(red: 0.56, green: 0.75, blue: 0.49),
                property: Color(red: 0.51, green: 0.65, blue: 0.60),
                builtin: Color(red: 1.00, green: 0.50, blue: 0.10)
            )
        case "Arc":
            return ThemePalette(
                text: Color(red: 0.95, green: 0.96, blue: 0.98),
                background: Color(red: 0.12, green: 0.13, blue: 0.16),
                cursor: Color(red: 0.35, green: 0.67, blue: 0.98),
                selection: Color(red: 0.22, green: 0.29, blue: 0.40),
                keyword: Color(red: 0.85, green: 0.48, blue: 1.0),
                string: Color(red: 0.45, green: 0.88, blue: 0.72),
                number: Color(red: 0.98, green: 0.75, blue: 0.37),
                comment: Color(red: 0.56, green: 0.60, blue: 0.70),
                type: Color(red: 0.44, green: 0.66, blue: 0.98),
                property: Color(red: 0.94, green: 0.61, blue: 0.32),
                builtin: Color(red: 0.99, green: 0.51, blue: 0.71)
            )
        case "Dusk":
            return ThemePalette(
                text: Color(red: 0.93, green: 0.92, blue: 0.95),
                background: Color(red: 0.14, green: 0.10, blue: 0.20),
                cursor: Color(red: 0.93, green: 0.54, blue: 0.94),
                selection: Color(red: 0.28, green: 0.22, blue: 0.39),
                keyword: Color(red: 0.98, green: 0.72, blue: 0.40),
                string: Color(red: 0.48, green: 0.89, blue: 0.67),
                number: Color(red: 0.98, green: 0.54, blue: 0.62),
                comment: Color(red: 0.62, green: 0.57, blue: 0.70),
                type: Color(red: 0.57, green: 0.75, blue: 1.0),
                property: Color(red: 0.88, green: 0.64, blue: 0.98),
                builtin: Color(red: 0.94, green: 0.44, blue: 0.76)
            )
        case "Aurora":
            return ThemePalette(
                text: Color(red: 0.92, green: 0.96, blue: 0.98),
                background: Color(red: 0.08, green: 0.12, blue: 0.14),
                cursor: Color(red: 0.35, green: 0.96, blue: 0.76),
                selection: Color(red: 0.18, green: 0.28, blue: 0.30),
                keyword: Color(red: 0.36, green: 0.92, blue: 0.98),
                string: Color(red: 0.44, green: 0.98, blue: 0.62),
                number: Color(red: 0.98, green: 0.76, blue: 0.38),
                comment: Color(red: 0.70, green: 0.86, blue: 0.92),
                type: Color(red: 0.52, green: 0.74, blue: 0.98),
                property: Color(red: 0.90, green: 0.60, blue: 0.98),
                builtin: Color(red: 0.98, green: 0.52, blue: 0.72)
            )
        case "Horizon":
            return ThemePalette(
                text: Color(red: 0.95, green: 0.94, blue: 0.92),
                background: Color(red: 0.14, green: 0.10, blue: 0.09),
                cursor: Color(red: 0.99, green: 0.62, blue: 0.36),
                selection: Color(red: 0.30, green: 0.20, blue: 0.18),
                keyword: Color(red: 0.99, green: 0.46, blue: 0.36),
                string: Color(red: 0.98, green: 0.78, blue: 0.36),
                number: Color(red: 0.98, green: 0.60, blue: 0.80),
                comment: Color(red: 0.86, green: 0.72, blue: 0.64),
                type: Color(red: 0.60, green: 0.78, blue: 0.98),
                property: Color(red: 0.96, green: 0.56, blue: 0.45),
                builtin: Color(red: 0.90, green: 0.72, blue: 0.36)
            )
        case "Midnight":
            return ThemePalette(
                text: Color(red: 0.90, green: 0.94, blue: 0.98),
                background: Color(red: 0.08, green: 0.10, blue: 0.16),
                cursor: Color(red: 0.25, green: 0.78, blue: 0.98),
                selection: Color(red: 0.16, green: 0.22, blue: 0.32),
                keyword: Color(red: 0.35, green: 0.86, blue: 0.96),
                string: Color(red: 0.48, green: 0.94, blue: 0.62),
                number: Color(red: 0.96, green: 0.77, blue: 0.31),
                comment: Color(red: 0.55, green: 0.63, blue: 0.74),
                type: Color(red: 0.40, green: 0.65, blue: 0.98),
                property: Color(red: 0.96, green: 0.56, blue: 0.45),
                builtin: Color(red: 0.86, green: 0.52, blue: 1.0)
            )
        case "Mono":
            return ThemePalette(
                text: Color(red: 0.88, green: 0.88, blue: 0.88),
                background: Color(red: 0.12, green: 0.12, blue: 0.12),
                cursor: Color.white,
                selection: Color(red: 0.26, green: 0.26, blue: 0.26),
                keyword: Color(red: 0.92, green: 0.92, blue: 0.92),
                string: Color(red: 0.80, green: 0.80, blue: 0.80),
                number: Color(red: 0.86, green: 0.86, blue: 0.86),
                comment: Color(red: 0.55, green: 0.55, blue: 0.55),
                type: Color(red: 0.90, green: 0.90, blue: 0.90),
                property: Color(red: 0.84, green: 0.84, blue: 0.84),
                builtin: Color(red: 0.78, green: 0.78, blue: 0.78)
            )
        case "Paper":
            return ThemePalette(
                text: Color(red: 0.12, green: 0.12, blue: 0.12),
                background: Color(red: 0.98, green: 0.97, blue: 0.94),
                cursor: Color(red: 0.16, green: 0.31, blue: 0.90),
                selection: Color(red: 0.86, green: 0.90, blue: 0.98),
                keyword: Color(red: 0.60, green: 0.18, blue: 0.82),
                string: Color(red: 0.12, green: 0.54, blue: 0.39),
                number: Color(red: 0.78, green: 0.37, blue: 0.09),
                comment: Color(red: 0.46, green: 0.46, blue: 0.46),
                type: Color(red: 0.12, green: 0.34, blue: 0.75),
                property: Color(red: 0.67, green: 0.27, blue: 0.52),
                builtin: Color(red: 0.74, green: 0.42, blue: 0.10)
            )
        case "Solar":
            return ThemePalette(
                text: Color(red: 0.98, green: 0.95, blue: 0.90),
                background: Color(red: 0.19, green: 0.12, blue: 0.08),
                cursor: Color(red: 0.99, green: 0.74, blue: 0.30),
                selection: Color(red: 0.33, green: 0.20, blue: 0.14),
                keyword: Color(red: 0.99, green: 0.64, blue: 0.24),
                string: Color(red: 0.98, green: 0.84, blue: 0.34),
                number: Color(red: 0.98, green: 0.52, blue: 0.74),
                comment: Color(red: 0.92, green: 0.80, blue: 0.66),
                type: Color(red: 0.52, green: 0.78, blue: 0.98),
                property: Color(red: 0.98, green: 0.58, blue: 0.38),
                builtin: Color(red: 0.94, green: 0.48, blue: 0.58)
            )
        case "Pulse":
            return ThemePalette(
                text: Color(red: 0.95, green: 0.96, blue: 0.98),
                background: Color(red: 0.10, green: 0.10, blue: 0.14),
                cursor: Color(red: 0.93, green: 0.45, blue: 0.57),
                selection: Color(red: 0.24, green: 0.18, blue: 0.28),
                keyword: Color(red: 0.98, green: 0.54, blue: 0.62),
                string: Color(red: 0.46, green: 0.92, blue: 0.83),
                number: Color(red: 0.96, green: 0.76, blue: 0.30),
                comment: Color(red: 0.62, green: 0.63, blue: 0.72),
                type: Color(red: 0.45, green: 0.72, blue: 0.98),
                property: Color(red: 0.96, green: 0.59, blue: 0.32),
                builtin: Color(red: 0.86, green: 0.52, blue: 1.0)
            )
        case "Mocha":
            return ThemePalette(
                text: Color(red: 0.95, green: 0.92, blue: 0.90),
                background: Color(red: 0.12, green: 0.09, blue: 0.08),
                cursor: Color(red: 0.82, green: 0.62, blue: 0.48),
                selection: Color(red: 0.22, green: 0.17, blue: 0.15),
                keyword: Color(red: 0.82, green: 0.60, blue: 0.98),
                string: Color(red: 0.84, green: 0.72, blue: 0.46),
                number: Color(red: 0.98, green: 0.70, blue: 0.46),
                comment: Color(red: 0.78, green: 0.70, blue: 0.66),
                type: Color(red: 0.52, green: 0.78, blue: 0.98),
                property: Color(red: 0.94, green: 0.56, blue: 0.32),
                builtin: Color(red: 0.90, green: 0.46, blue: 0.72)
            )
        case "Custom":
            let text = colorFromHex(defaults.string(forKey: "SettingsThemeTextColor") ?? "#EDEDED", fallback: .white)
            let background = colorFromHex(defaults.string(forKey: "SettingsThemeBackgroundColor") ?? "#0E1116", fallback: .black)
            let cursor = colorFromHex(defaults.string(forKey: "SettingsThemeCursorColor") ?? "#4EA4FF", fallback: .blue)
            let selection = colorFromHex(defaults.string(forKey: "SettingsThemeSelectionColor") ?? "#2A3340", fallback: .gray)
            let keyword = colorFromHex(defaults.string(forKey: "SettingsThemeKeywordColor") ?? "#F5D90A", fallback: .yellow)
            let string = colorFromHex(defaults.string(forKey: "SettingsThemeStringColor") ?? "#4EA4FF", fallback: .blue)
            let number = colorFromHex(defaults.string(forKey: "SettingsThemeNumberColor") ?? "#FFB86C", fallback: .orange)
            let comment = colorFromHex(defaults.string(forKey: "SettingsThemeCommentColor") ?? "#7F8C98", fallback: .gray)
            let type = colorFromHex(defaults.string(forKey: "SettingsThemeTypeColor") ?? "#32D269", fallback: .green)
            let builtin = colorFromHex(defaults.string(forKey: "SettingsThemeBuiltinColor") ?? "#EC7887", fallback: .red)
            return ThemePalette(
                text: text,
                background: background,
                cursor: cursor,
                selection: selection,
                keyword: keyword,
                string: string,
                number: number,
                comment: comment,
                type: type,
                property: string,
                builtin: builtin
            )
        default:
            return ThemePalette(
                text: Color(red: 0.93, green: 0.95, blue: 0.98),
                background: Color(red: 0.10, green: 0.11, blue: 0.14),
                cursor: Color(red: 0.31, green: 0.72, blue: 0.99),
                selection: Color(red: 0.22, green: 0.30, blue: 0.43),
                keyword: Color(red: 0.96, green: 0.84, blue: 0.23),
                string: Color(red: 0.98, green: 0.48, blue: 0.82),
                number: Color(red: 0.98, green: 0.72, blue: 0.33),
                comment: Color(red: 0.60, green: 0.66, blue: 0.74),
                type: Color(red: 0.41, green: 0.69, blue: 0.99),
                property: Color(red: 0.39, green: 0.90, blue: 0.72),
                builtin: Color(red: 0.94, green: 0.42, blue: 0.66)
            )
        }
    }()
    return ThemePalette(
        text: palette.text,
        background: palette.background,
        cursor: palette.cursor,
        selection: emphasizedSelectionColor(for: canonicalName, fallback: palette.selection),
        keyword: palette.keyword,
        string: palette.string,
        number: palette.number,
        comment: palette.comment,
        type: palette.type,
        property: palette.property,
        builtin: palette.builtin
    )
}

func themePaletteColors(for name: String, defaults: UserDefaults = .standard) -> ThemePaletteColors {
    let palette = paletteForThemeName(canonicalThemeName(name), defaults: defaults)
    return ThemePaletteColors(
        text: palette.text,
        background: palette.background,
        cursor: palette.cursor,
        selection: palette.selection,
        keyword: palette.keyword,
        string: palette.string,
        number: palette.number,
        comment: palette.comment,
        type: palette.type,
        property: palette.property,
        builtin: palette.builtin
    )
}

func currentEditorTheme(colorScheme: ColorScheme) -> EditorTheme {
    let defaults = UserDefaults.standard
    // Always respect the user's selected theme across iOS and macOS.
    let name = canonicalThemeName(defaults.string(forKey: "SettingsThemeName") ?? "Neon Glow")
    let boldKeywords = defaults.bool(forKey: "SettingsThemeBoldKeywords")
    let italicComments = defaults.bool(forKey: "SettingsThemeItalicComments")
    let underlineLinks = defaults.bool(forKey: "SettingsThemeUnderlineLinks")
    let boldMarkdownHeadings = defaults.bool(forKey: "SettingsThemeBoldMarkdownHeadings")
    let palette = paletteForThemeName(name, defaults: defaults)
    // Keep base editor text legible and consistent across all themes.
    // Neon Glow gets a slightly brighter dark-mode text tone.
    let baseTextColor: Color = {
        if name == "Neon Glow" {
            return colorScheme == .light
                ? .black
                : Color(red: 0.94, green: 0.94, blue: 0.94)
        }
        if colorScheme == .light { return .black }
        return Color(red: 0.90, green: 0.90, blue: 0.90)
    }()

    let profile: SyntaxAdjustmentProfile = {
        let vividNeonThemes: Set<String> = [
            "Neon Glow",
            "Neon Voltage",
            "Laserwave",
            "Cyber Lime",
            "Plasma Storm",
            "Inferno Neon",
            "Ultraviolet Flux",
            "Custom"
        ]
        return vividNeonThemes.contains(name) ? .neonRaw : .standard
    }()
    let keyword = adjustedSyntaxColor(
        palette.keyword,
        colorScheme: colorScheme,
        profile: profile,
        darkenInLight: 0.30,
        brightenInDark: 0.08
    )
    let string = adjustedSyntaxColor(
        palette.string,
        colorScheme: colorScheme,
        profile: profile,
        darkenInLight: 0.40,
        brightenInDark: 0.10
    )
    let number = adjustedSyntaxColor(
        palette.number,
        colorScheme: colorScheme,
        profile: profile,
        darkenInLight: 0.28,
        brightenInDark: 0.08
    )
    let comment = adjustedSyntaxColor(
        palette.comment,
        colorScheme: colorScheme,
        profile: profile,
        darkenInLight: 0.28,
        brightenInDark: 0.20
    )
    let type = adjustedSyntaxColor(
        palette.type,
        colorScheme: colorScheme,
        profile: profile,
        darkenInLight: 0.30,
        brightenInDark: 0.08
    )
    let property = adjustedSyntaxColor(
        palette.property,
        colorScheme: colorScheme,
        profile: profile,
        darkenInLight: 0.32,
        brightenInDark: 0.08
    )
    let builtin = adjustedSyntaxColor(
        palette.builtin,
        colorScheme: colorScheme,
        profile: profile,
        darkenInLight: 0.30,
        brightenInDark: 0.08
    )

    let syntax = SyntaxColors(
        keyword: keyword,
        string: string,
        number: number,
        comment: comment,
        attribute: property,
        variable: property,
        def: type,
        property: property,
        meta: builtin,
        tag: keyword,
        atom: builtin,
        builtin: builtin,
        type: type
    )

    return EditorTheme(
        text: baseTextColor,
        background: modeAdjustedEditorBackground(palette.background, colorScheme: colorScheme),
        cursor: palette.cursor,
        selection: palette.selection,
        syntax: syntax,
        boldKeywords: boldKeywords,
        italicComments: italicComments,
        underlineLinks: underlineLinks,
        boldMarkdownHeadings: boldMarkdownHeadings
    )
}

func colorFromHex(_ hex: String, fallback: Color) -> Color {
    let cleaned = hex.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "#", with: "")
    guard (cleaned.count == 6 || cleaned.count == 8), let intVal = UInt64(cleaned, radix: 16) else { return fallback }
    if cleaned.count == 8 {
        let r = Double((intVal >> 24) & 0xFF) / 255.0
        let g = Double((intVal >> 16) & 0xFF) / 255.0
        let b = Double((intVal >> 8) & 0xFF) / 255.0
        let a = Double(intVal & 0xFF) / 255.0
        return Color(red: r, green: g, blue: b, opacity: a)
    }
    let r = Double((intVal >> 16) & 0xFF) / 255.0
    let g = Double((intVal >> 8) & 0xFF) / 255.0
    let b = Double(intVal & 0xFF) / 255.0
    return Color(red: r, green: g, blue: b)
}

func colorToHex(_ color: Color) -> String {
#if os(macOS)
    let platform = PlatformColor(color)
    guard let srgb = platform.usingColorSpace(.sRGB) else { return "#FFFFFF" }
    let r = Int(round(srgb.redComponent * 255))
    let g = Int(round(srgb.greenComponent * 255))
    let b = Int(round(srgb.blueComponent * 255))
    let a = Int(round(srgb.alphaComponent * 255))
#else
    let platform = PlatformColor(color)
    var r: CGFloat = 0
    var g: CGFloat = 0
    var b: CGFloat = 0
    var a: CGFloat = 0
    platform.getRed(&r, green: &g, blue: &b, alpha: &a)
    let rInt = Int(round(r * 255))
    let gInt = Int(round(g * 255))
    let bInt = Int(round(b * 255))
    let aInt = Int(round(a * 255))
#endif
#if os(macOS)
    if a < 255 {
        return String(format: "#%02X%02X%02X%02X", r, g, b, a)
    }
    return String(format: "#%02X%02X%02X", r, g, b)
#else
    if aInt < 255 {
        return String(format: "#%02X%02X%02X%02X", rInt, gInt, bInt, aInt)
    }
    return String(format: "#%02X%02X%02X", rInt, gInt, bInt)
#endif
}
