import Foundation
import AppKit

/// Central registry of all user-configurable notch settings.
enum NotchSettings {

    enum Keys {
        // General
        static let showOnAllDisplays     = "showOnAllDisplays"
        static let openNotchOnHover      = "openNotchOnHover"
        static let minimumHoverDuration  = "minimumHoverDuration"
        static let showMenuBarIcon       = "showMenuBarIcon"
        static let launchAtLogin         = "launchAtLogin"

        // Sizing — closed state
        static let notchHeight           = "notchHeight"
        static let nonNotchHeight        = "nonNotchHeight"
        static let notchHeightMode       = "notchHeightMode"
        static let nonNotchHeightMode    = "nonNotchHeightMode"

        // Sizing — open state
        static let openWidth             = "openWidth"
        static let openHeight            = "openHeight"

        // Appearance
        static let enableShadow          = "enableShadow"
        static let shadowRadius          = "shadowRadius"
        static let shadowOpacity         = "shadowOpacity"
        static let cornerRadiusScaling   = "cornerRadiusScaling"
        static let notchOpacity          = "notchOpacity"
        static let blurRadius            = "blurRadius"

        // Animation
        static let openSpringResponse    = "openSpringResponse"
        static let openSpringDamping     = "openSpringDamping"
        static let closeSpringResponse   = "closeSpringResponse"
        static let closeSpringDamping    = "closeSpringDamping"
        static let hoverSpringResponse   = "hoverSpringResponse"
        static let hoverSpringDamping    = "hoverSpringDamping"
        static let resizeAnimationDuration = "resizeAnimationDuration"

        // Behavior
        static let enableGestures        = "enableGestures"
        static let gestureSensitivity    = "gestureSensitivity"

        // Terminal
        static let terminalFontSize      = "terminalFontSize"
        static let terminalShell         = "terminalShell"
        static let terminalTheme         = "terminalTheme"
        static let terminalSizePresets   = "terminalSizePresets"
        static let workspaceSummaries    = "workspaceSummaries"
        static let screenAssignments     = "screenAssignments"

        // Hotkeys — each stores a HotkeyBinding JSON string
        static let hotkeyToggle          = "hotkey_toggle"
        static let hotkeyNewTab          = "hotkey_newTab"
        static let hotkeyCloseTab        = "hotkey_closeTab"
        static let hotkeyNextTab         = "hotkey_nextTab"
        static let hotkeyPreviousTab     = "hotkey_previousTab"
        static let hotkeyNextWorkspace   = "hotkey_nextWorkspace"
        static let hotkeyPreviousWorkspace = "hotkey_previousWorkspace"
        static let hotkeyDetachTab       = "hotkey_detachTab"
    }

    enum Defaults {
        static let showOnAllDisplays: Bool      = true
        static let openNotchOnHover: Bool       = true
        static let minimumHoverDuration: Double = 0.3
        static let showMenuBarIcon: Bool        = true
        static let launchAtLogin: Bool          = false

        static let notchHeight: Double          = 32
        static let nonNotchHeight: Double       = 32
        static let notchHeightMode: Int         = 0
        static let nonNotchHeightMode: Int      = 1

        static let openWidth: Double            = 640
        static let openHeight: Double           = 350

        static let enableShadow: Bool           = true
        static let shadowRadius: Double         = 6
        static let shadowOpacity: Double        = 0.5
        static let cornerRadiusScaling: Bool    = true
        static let notchOpacity: Double         = 1.0
        static let blurRadius: Double           = 0

        static let openSpringResponse: Double   = 0.42
        static let openSpringDamping: Double    = 0.8
        static let closeSpringResponse: Double  = 0.45
        static let closeSpringDamping: Double   = 1.0
        static let hoverSpringResponse: Double  = 0.38
        static let hoverSpringDamping: Double   = 0.8
        static let resizeAnimationDuration: Double = 0.42

        static let enableGestures: Bool         = true
        static let gestureSensitivity: Double   = 0.5

        static let terminalFontSize: Double     = 13
        static let terminalShell: String        = ""
        static let terminalTheme: String        = TerminalTheme.terminalApp.rawValue
        static let terminalSizePresets: String  = TerminalSizePresetStore.defaultPresetsJSON()

        // Default hotkey bindings as JSON
        static let hotkeyToggle: String      = HotkeyBinding.cmdReturn.toJSON()
        static let hotkeyNewTab: String      = HotkeyBinding.cmdT.toJSON()
        static let hotkeyCloseTab: String    = HotkeyBinding.cmdW.toJSON()
        static let hotkeyNextTab: String     = HotkeyBinding.cmdShiftRB.toJSON()
        static let hotkeyPreviousTab: String = HotkeyBinding.cmdShiftLB.toJSON()
        static let hotkeyNextWorkspace: String = HotkeyBinding.cmdShiftDown.toJSON()
        static let hotkeyPreviousWorkspace: String = HotkeyBinding.cmdShiftUp.toJSON()
        static let hotkeyDetachTab: String   = HotkeyBinding.cmdD.toJSON()
    }

    static func registerDefaults() {
        UserDefaults.standard.register(defaults: [
            Keys.showOnAllDisplays:     Defaults.showOnAllDisplays,
            Keys.openNotchOnHover:      Defaults.openNotchOnHover,
            Keys.minimumHoverDuration:  Defaults.minimumHoverDuration,
            Keys.showMenuBarIcon:       Defaults.showMenuBarIcon,
            Keys.launchAtLogin:         Defaults.launchAtLogin,

            Keys.notchHeight:           Defaults.notchHeight,
            Keys.nonNotchHeight:        Defaults.nonNotchHeight,
            Keys.notchHeightMode:       Defaults.notchHeightMode,
            Keys.nonNotchHeightMode:    Defaults.nonNotchHeightMode,

            Keys.openWidth:             Defaults.openWidth,
            Keys.openHeight:            Defaults.openHeight,

            Keys.enableShadow:          Defaults.enableShadow,
            Keys.shadowRadius:          Defaults.shadowRadius,
            Keys.shadowOpacity:         Defaults.shadowOpacity,
            Keys.cornerRadiusScaling:   Defaults.cornerRadiusScaling,
            Keys.notchOpacity:          Defaults.notchOpacity,
            Keys.blurRadius:            Defaults.blurRadius,

            Keys.openSpringResponse:    Defaults.openSpringResponse,
            Keys.openSpringDamping:     Defaults.openSpringDamping,
            Keys.closeSpringResponse:   Defaults.closeSpringResponse,
            Keys.closeSpringDamping:    Defaults.closeSpringDamping,
            Keys.hoverSpringResponse:   Defaults.hoverSpringResponse,
            Keys.hoverSpringDamping:    Defaults.hoverSpringDamping,
            Keys.resizeAnimationDuration: Defaults.resizeAnimationDuration,

            Keys.enableGestures:        Defaults.enableGestures,
            Keys.gestureSensitivity:    Defaults.gestureSensitivity,

            Keys.terminalFontSize:      Defaults.terminalFontSize,
            Keys.terminalShell:         Defaults.terminalShell,
            Keys.terminalTheme:         Defaults.terminalTheme,
            Keys.terminalSizePresets:   Defaults.terminalSizePresets,

            Keys.hotkeyToggle:          Defaults.hotkeyToggle,
            Keys.hotkeyNewTab:          Defaults.hotkeyNewTab,
            Keys.hotkeyCloseTab:        Defaults.hotkeyCloseTab,
            Keys.hotkeyNextTab:         Defaults.hotkeyNextTab,
            Keys.hotkeyPreviousTab:     Defaults.hotkeyPreviousTab,
            Keys.hotkeyNextWorkspace:   Defaults.hotkeyNextWorkspace,
            Keys.hotkeyPreviousWorkspace: Defaults.hotkeyPreviousWorkspace,
            Keys.hotkeyDetachTab:       Defaults.hotkeyDetachTab,
        ])
    }
}

enum NotchHeightMode: Int, CaseIterable, Identifiable {
    case matchRealNotchSize = 0
    case matchMenuBar       = 1
    case custom             = 2

    var id: Int { rawValue }
    var label: String {
        switch self {
        case .matchRealNotchSize: return "Match Notch"
        case .matchMenuBar:       return "Match Menu Bar"
        case .custom:             return "Custom"
        }
    }
}

enum NonNotchHeightMode: Int, CaseIterable, Identifiable {
    case matchMenuBar = 1
    case custom       = 2

    var id: Int { rawValue }
    var label: String {
        switch self {
        case .matchMenuBar: return "Match Menu Bar"
        case .custom:       return "Custom"
        }
    }
}

struct TerminalSizePreset: Codable, Equatable, Identifiable {
    var id: UUID
    var name: String
    var width: Double
    var height: Double
    var hotkey: HotkeyBinding?

    init(
        id: UUID = UUID(),
        name: String,
        width: Double,
        height: Double,
        hotkey: HotkeyBinding? = nil
    ) {
        self.id = id
        self.name = name
        self.width = width
        self.height = height
        self.hotkey = hotkey
    }

    var size: CGSize {
        CGSize(width: width, height: height)
    }
}

enum TerminalSizePresetStore {
    static func load() -> [TerminalSizePreset] {
        let defaults = UserDefaults.standard
        guard let json = defaults.string(forKey: NotchSettings.Keys.terminalSizePresets),
              let presets = decodePresets(from: json) else {
            return defaultPresets()
        }
        return presets
    }

    static func save(_ presets: [TerminalSizePreset]) {
        UserDefaults.standard.set(encodePresets(presets), forKey: NotchSettings.Keys.terminalSizePresets)
    }

    static func reset() {
        save(defaultPresets())
    }

    static func loadDefaults() -> [TerminalSizePreset] {
        defaultPresets()
    }

    static func defaultPresetsJSON() -> String {
        encodePresets(defaultPresets())
    }

    static func suggestedHotkey(for presets: [TerminalSizePreset]) -> HotkeyBinding? {
        let used = Set(presets.compactMap(\.hotkey))
        for digit in 1...9 {
            guard let candidate = HotkeyBinding.cmdShiftDigit(digit) else { continue }
            if !used.contains(candidate) {
                return candidate
            }
        }
        return nil
    }

    private static func defaultPresets() -> [TerminalSizePreset] {
        [
            TerminalSizePreset(name: "Compact", width: 480, height: 220, hotkey: HotkeyBinding.cmdShiftDigit(1)),
            TerminalSizePreset(name: "Default", width: 640, height: 350, hotkey: HotkeyBinding.cmdShiftDigit(2)),
            TerminalSizePreset(name: "Large", width: 900, height: 500, hotkey: HotkeyBinding.cmdShiftDigit(3)),
        ]
    }

    static func decodePresets(from json: String) -> [TerminalSizePreset]? {
        guard let data = json.data(using: .utf8) else { return nil }
        return try? JSONDecoder().decode([TerminalSizePreset].self, from: data)
    }

    static func encodePresets(_ presets: [TerminalSizePreset]) -> String {
        guard let data = try? JSONEncoder().encode(presets),
              let json = String(data: data, encoding: .utf8) else {
            return "[]"
        }
        return json
    }
}
