//
//  GlobalLayoutSettings.swift
//  Yattee
//
//  Global settings that apply to all control buttons in the player layout.
//

import Foundation
import SwiftUI

/// Theme options for player controls appearance.
enum ControlsTheme: String, Codable, Hashable, Sendable, CaseIterable {
    /// Follow system appearance
    case system

    /// Always use light appearance
    case light

    /// Always use dark appearance
    case dark

    /// Localized display name.
    var displayName: String {
        switch self {
        case .system:
            return String(localized: "controls.theme.system")
        case .light:
            return String(localized: "controls.theme.light")
        case .dark:
            return String(localized: "controls.theme.dark")
        }
    }

    /// Returns the ColorScheme to apply, or nil for system.
    var colorScheme: ColorScheme? {
        switch self {
        case .system:
            return nil
        case .light:
            return .light
        case .dark:
            return .dark
        }
    }
}

/// Size options for control buttons.
enum ButtonSize: String, Codable, Hashable, Sendable, CaseIterable {
    case small
    case medium
    case large

    /// The point size for buttons at this size.
    var pointSize: CGFloat {
        switch self {
        case .small:
            return 36
        case .medium:
            return 44
        case .large:
            return 52
        }
    }

    /// The icon size relative to button size.
    var iconSize: CGFloat {
        switch self {
        case .small:
            return 18
        case .medium:
            return 22
        case .large:
            return 26
        }
    }

    /// Localized display name.
    var displayName: String {
        switch self {
        case .small:
            return String(localized: "controls.size.small")
        case .medium:
            return String(localized: "controls.size.medium")
        case .large:
            return String(localized: "controls.size.large")
        }
    }
}

/// Background style options for control buttons.
enum ButtonBackgroundStyle: String, Codable, Hashable, Sendable, CaseIterable {
    /// No background (default, current behavior)
    case none

    /// Clear glass - subtle circular glass background
    case clearGlass

    /// Regular glass - more visible circular glass background
    case regularGlass

    /// Localized display name.
    var displayName: String {
        switch self {
        case .none:
            return String(localized: "controls.buttonBackground.none")
        case .clearGlass:
            return String(localized: "controls.buttonBackground.clearGlass")
        case .regularGlass:
            return String(localized: "controls.buttonBackground.regularGlass")
        }
    }

    /// The GlassStyle to use for this background style.
    var glassStyle: GlassStyle? {
        switch self {
        case .none:
            return nil
        case .clearGlass:
            return .clear
        case .regularGlass:
            return .regular
        }
    }
}

/// Overall style for player controls appearance.
enum ControlsStyle: String, Codable, Hashable, Sendable, CaseIterable {
    /// No background, follows system theme
    case plain

    /// Regular glass background, always dark theme
    case glass

    /// Localized display name.
    var displayName: String {
        switch self {
        case .plain:
            return String(localized: "controls.style.plain")
        case .glass:
            return String(localized: "controls.style.glass")
        }
    }

    /// The theme to apply for this style.
    var theme: ControlsTheme {
        switch self {
        case .plain:
            return .system
        case .glass:
            return .dark
        }
    }

    /// The button background to apply for this style.
    var buttonBackground: ButtonBackgroundStyle {
        switch self {
        case .plain:
            return .none
        case .glass:
            return .regularGlass
        }
    }
}

// MARK: - Codable Color

/// A color stored as RGBA components for Codable support.
struct CodableColor: Codable, Hashable, Sendable {
    var red: Double
    var green: Double
    var blue: Double
    var opacity: Double

    /// Creates a CodableColor from RGBA components.
    /// - Parameters:
    ///   - red: Red component (0-1).
    ///   - green: Green component (0-1).
    ///   - blue: Blue component (0-1).
    ///   - opacity: Opacity component (0-1). Defaults to 1.
    init(red: Double, green: Double, blue: Double, opacity: Double = 1.0) {
        self.red = red
        self.green = green
        self.blue = blue
        self.opacity = opacity
    }

    /// Creates a CodableColor from a SwiftUI Color.
    /// - Parameter color: The SwiftUI Color to convert.
    init(_ color: Color) {
        let resolved = color.resolve(in: EnvironmentValues())
        self.red = Double(resolved.red)
        self.green = Double(resolved.green)
        self.blue = Double(resolved.blue)
        self.opacity = Double(resolved.opacity)
    }

    /// Converts to a SwiftUI Color.
    var color: Color {
        Color(red: red, green: green, blue: blue, opacity: opacity)
    }
}

// MARK: - SponsorBlock Segment Settings

/// Settings for a single SponsorBlock category's appearance on the progress bar.
struct SponsorBlockCategorySettings: Codable, Hashable, Sendable {
    /// Whether this category is visible on the progress bar.
    var isVisible: Bool

    /// The color to use for this category's segments.
    var color: CodableColor

    /// Creates category settings.
    /// - Parameters:
    ///   - isVisible: Whether the category is visible. Defaults to `true`.
    ///   - color: The color for segments. Defaults to gray.
    init(isVisible: Bool = true, color: CodableColor = CodableColor(red: 0.5, green: 0.5, blue: 0.5)) {
        self.isVisible = isVisible
        self.color = color
    }
}

/// Settings for SponsorBlock segment display on the progress bar.
struct SponsorBlockSegmentSettings: Codable, Hashable, Sendable {
    /// Global toggle for showing sponsor segments on the progress bar.
    var showSegments: Bool

    /// Per-category settings, keyed by SponsorBlockCategory raw value.
    var categorySettings: [String: SponsorBlockCategorySettings]

    /// Creates sponsor block segment settings.
    /// - Parameters:
    ///   - showSegments: Whether to show segments. Defaults to `true`.
    ///   - categorySettings: Per-category settings. Defaults to default colors for all categories.
    init(
        showSegments: Bool = true,
        categorySettings: [String: SponsorBlockCategorySettings]? = nil
    ) {
        self.showSegments = showSegments
        self.categorySettings = categorySettings ?? Self.defaultCategorySettings
    }

    /// Default settings with all categories visible using their default overlay colors.
    static let `default` = SponsorBlockSegmentSettings()

    /// Default category settings using default colors for each category.
    /// Colors match the SponsorBlockCategory.overlayColor values.
    static var defaultCategorySettings: [String: SponsorBlockCategorySettings] {
        [
            "sponsor": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.green)),
            "selfpromo": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.yellow)),
            "interaction": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.purple)),
            "intro": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.cyan)),
            "outro": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.blue)),
            "preview": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.indigo)),
            "music_offtopic": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.orange)),
            "filler": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.gray)),
            "poi_highlight": SponsorBlockCategorySettings(isVisible: true, color: CodableColor(.pink)),
        ]
    }

    /// Gets settings for a specific category key, returning defaults if not found.
    /// - Parameter categoryKey: The category raw value key.
    /// - Returns: The category settings.
    func settings(forKey categoryKey: String) -> SponsorBlockCategorySettings {
        categorySettings[categoryKey] ?? Self.defaultCategorySettings[categoryKey]
            ?? SponsorBlockCategorySettings()
    }

    /// Returns a copy with updated settings for a specific category key.
    /// - Parameters:
    ///   - categoryKey: The category raw value key.
    ///   - settings: The new settings.
    /// - Returns: Updated SponsorBlockSegmentSettings.
    func withUpdatedSettings(
        forKey categoryKey: String,
        _ settings: SponsorBlockCategorySettings
    ) -> SponsorBlockSegmentSettings {
        var newCategorySettings = categorySettings
        newCategorySettings[categoryKey] = settings
        return SponsorBlockSegmentSettings(
            showSegments: showSegments,
            categorySettings: newCategorySettings
        )
    }
}

// MARK: - SponsorBlockCategory Integration

extension SponsorBlockSegmentSettings {
    /// Gets settings for a specific category, returning defaults if not found.
    /// - Parameter category: The category to get settings for.
    /// - Returns: The category settings.
    func settings(for category: SponsorBlockCategory) -> SponsorBlockCategorySettings {
        settings(forKey: category.rawValue)
    }

    /// Returns a copy with updated settings for a specific category.
    /// - Parameters:
    ///   - category: The category to update.
    ///   - settings: The new settings.
    /// - Returns: Updated SponsorBlockSegmentSettings.
    func withUpdatedSettings(
        for category: SponsorBlockCategory,
        _ settings: SponsorBlockCategorySettings
    ) -> SponsorBlockSegmentSettings {
        withUpdatedSettings(forKey: category.rawValue, settings)
    }
}

// MARK: - Font Style

/// Font style options for player controls text.
enum ControlsFontStyle: String, Codable, Hashable, Sendable, CaseIterable {
    /// System default font with monospaced digits.
    case system

    /// Fully monospaced font.
    case monospaced

    /// Rounded system font with monospaced digits.
    case rounded

    /// Localized display name.
    var displayName: String {
        switch self {
        case .system:
            return String(localized: "controls.fontStyle.system")
        case .monospaced:
            return String(localized: "controls.fontStyle.monospaced")
        case .rounded:
            return String(localized: "controls.fontStyle.rounded")
        }
    }

    /// Returns a Font for the given text style.
    func font(_ style: Font.TextStyle = .caption) -> Font {
        switch self {
        case .system:
            return .system(style).monospacedDigit()
        case .monospaced:
            return .system(style, design: .monospaced)
        case .rounded:
            return .system(style, design: .rounded).monospacedDigit()
        }
    }

    /// Returns a Font for the given size and weight.
    func font(size: CGFloat, weight: Font.Weight = .regular) -> Font {
        switch self {
        case .system:
            return .system(size: size, weight: weight).monospacedDigit()
        case .monospaced:
            return .system(size: size, weight: weight, design: .monospaced)
        case .rounded:
            return .system(size: size, weight: weight, design: .rounded).monospacedDigit()
        }
    }
}

/// Settings for the progress bar appearance.
struct ProgressBarSettings: Codable, Hashable, Sendable {
    /// Color for the played portion of the progress bar.
    var playedColor: CodableColor

    /// Whether to show chapter markers on the progress bar.
    var showChapters: Bool

    /// Settings for SponsorBlock segment display.
    var sponsorBlockSettings: SponsorBlockSegmentSettings

    /// Creates progress bar settings.
    /// - Parameters:
    ///   - playedColor: Color for the played portion. Defaults to red.
    ///   - showChapters: Whether to show chapter markers. Defaults to `true`.
    ///   - sponsorBlockSettings: Settings for SponsorBlock segments. Defaults to `.default`.
    init(
        playedColor: CodableColor = CodableColor(.red),
        showChapters: Bool = true,
        sponsorBlockSettings: SponsorBlockSegmentSettings = .default
    ) {
        self.playedColor = playedColor
        self.showChapters = showChapters
        self.sponsorBlockSettings = sponsorBlockSettings
    }

    /// Default progress bar settings.
    static let `default` = ProgressBarSettings()
}

/// Global settings that apply to all control buttons.
struct GlobalLayoutSettings: Codable, Hashable, Sendable {
    /// Overall style for player controls.
    var style: ControlsStyle

    /// Size of control buttons.
    var buttonSize: ButtonSize

    /// Font style for text elements in player controls.
    var fontStyle: ControlsFontStyle

    /// Opacity of the controls background fade (0-1).
    var controlsFadeOpacity: Double

    /// Mode for system control buttons (Control Center, Lock Screen).
    var systemControlsMode: SystemControlsMode

    /// Duration for seek operations when systemControlsMode is .seek.
    var systemControlsSeekDuration: SystemControlsSeekDuration

    /// How volume is controlled during playback.
    var volumeMode: VolumeMode

    /// Forced appearance for the macOS control bar's glass background.
    var controlBarTheme: ControlsTheme

    /// Theme derived from style (for backwards compatibility).
    var theme: ControlsTheme { style.theme }

    /// Button background derived from style (for backwards compatibility).
    var buttonBackground: ButtonBackgroundStyle { style.buttonBackground }

    /// Cached settings for instant access (avoids flash on view recreation).
    /// Updated whenever settings are loaded from the active preset.
    nonisolated(unsafe) static var cached: GlobalLayoutSettings = .default

    // MARK: - Codable

    private enum CodingKeys: String, CodingKey {
        case style
        case buttonSize
        case buttonBackground // Legacy, not used for encoding
        case theme // Legacy, not used for encoding
        case fontStyle
        case controlsFadeOpacity
        case systemControlsMode
        case systemControlsSeekDuration
        case volumeMode
        case controlBarTheme
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        style = try container.decodeIfPresent(ControlsStyle.self, forKey: .style) ?? .glass
        buttonSize = try container.decodeIfPresent(ButtonSize.self, forKey: .buttonSize) ?? .medium
        fontStyle = try container.decodeIfPresent(ControlsFontStyle.self, forKey: .fontStyle) ?? .system
        controlsFadeOpacity = try container.decodeIfPresent(Double.self, forKey: .controlsFadeOpacity) ?? 0.5
        systemControlsMode = try container.decodeIfPresent(SystemControlsMode.self, forKey: .systemControlsMode) ?? .seek
        systemControlsSeekDuration = try container.decodeIfPresent(SystemControlsSeekDuration.self, forKey: .systemControlsSeekDuration) ?? .tenSeconds
        volumeMode = try container.decodeIfPresent(VolumeMode.self, forKey: .volumeMode) ?? .mpv
        controlBarTheme = try container.decodeIfPresent(ControlsTheme.self, forKey: .controlBarTheme) ?? .system
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(style, forKey: .style)
        try container.encode(buttonSize, forKey: .buttonSize)
        try container.encode(fontStyle, forKey: .fontStyle)
        try container.encode(controlsFadeOpacity, forKey: .controlsFadeOpacity)
        try container.encode(systemControlsMode, forKey: .systemControlsMode)
        try container.encode(systemControlsSeekDuration, forKey: .systemControlsSeekDuration)
        try container.encode(volumeMode, forKey: .volumeMode)
        try container.encode(controlBarTheme, forKey: .controlBarTheme)
    }

    // MARK: - Initialization

    /// Creates global layout settings.
    init(
        style: ControlsStyle = .glass,
        buttonSize: ButtonSize = .medium,
        fontStyle: ControlsFontStyle = .system,
        controlsFadeOpacity: Double = 0.5,
        systemControlsMode: SystemControlsMode = .seek,
        systemControlsSeekDuration: SystemControlsSeekDuration = .tenSeconds,
        volumeMode: VolumeMode = .mpv,
        controlBarTheme: ControlsTheme = .system
    ) {
        self.style = style
        self.buttonSize = buttonSize
        self.fontStyle = fontStyle
        self.controlsFadeOpacity = controlsFadeOpacity
        self.systemControlsMode = systemControlsMode
        self.systemControlsSeekDuration = systemControlsSeekDuration
        self.volumeMode = volumeMode
        self.controlBarTheme = controlBarTheme
    }

    // MARK: - Defaults

    /// Default global layout settings.
    static let `default` = GlobalLayoutSettings()
}
