//
//  ClockStyle.swift
//  TheNoiseClock
//
//  Created by Matt Bruce on 9/7/25.
//

import SwiftUI
import Observation
import Bedrock

/// Clock customization settings and data model
@Observable
class ClockStyle: Codable, Equatable {
    
    // MARK: - Time Format Settings
    var use24Hour: Bool = true
    var showSeconds: Bool = false
    var showAmPm: Bool = true
    var forceHorizontalMode: Bool = false // Force horizontal layout even in portrait
    
    // MARK: - Visual Settings
    var digitColorHex: String = AppConstants.Defaults.digitColorHex
    var randomizeColor: Bool = false
    var glowIntensity: Double = AppConstants.Defaults.glowIntensity
    var digitScale: Double = AppConstants.Defaults.digitScale
    var stretched: Bool = true
    var backgroundHex: String = AppConstants.Defaults.backgroundColorHex
    
    // MARK: - Color Theme Settings
    var selectedColorTheme: String = "Custom" // Custom, Night, Day, Red, Orange, Yellow, Green, Blue, Purple, Pink, White
    
    // MARK: - Night Mode Settings
    var nightModeEnabled: Bool = false
    var autoNightMode: Bool = false
    var scheduledNightMode: Bool = false
    var nightModeStartTime: String = "22:00" // 10:00 PM
    var nightModeEndTime: String = "06:00"   // 6:00 AM
    var autoBrightness: Bool = true // Automatically dim brightness in night mode
    var ambientLightThreshold: Double = 0.3 // Threshold for ambient light detection (0.0-1.0)
    
    // MARK: - Font Settings
    var fontFamily: FontFamily = .system
    var fontWeight: Font.Weight = .bold
    var fontDesign: Font.Design = .rounded
    
    // MARK: - Overlay Settings
    var showBattery: Bool = true
    var showDate: Bool = true
    var dateFormat: String = "d MMMM EEE" // Default: "7 September Mon"
    var clockOpacity: Double = AppConstants.Defaults.clockOpacity
    var overlayOpacity: Double = AppConstants.Defaults.overlayOpacity
    
    // MARK: - Display Settings
    var keepAwake: Bool = false // Keep screen awake in display mode
    var respectFocusModes: Bool = true // Respect Focus mode settings for audio
    
    // MARK: - Cached Colors
    private var _cachedDigitColor: Color?
    private var _cachedBackgroundColor: Color?
    
    // MARK: - Codable Keys
    private enum CodingKeys: String, CodingKey {
        case use24Hour
        case showSeconds
        case showAmPm
        case forceHorizontalMode
        case digitColorHex
        case randomizeColor
        case glowIntensity
        case digitScale
        case stretched
        case backgroundHex
        case selectedColorTheme
        case nightModeEnabled
        case autoNightMode
        case scheduledNightMode
        case nightModeStartTime
        case nightModeEndTime
        case autoBrightness
        case ambientLightThreshold
        case fontFamily
        case fontWeight
        case fontDesign
        case showBattery
        case showDate
        case dateFormat
        case clockOpacity
        case overlayOpacity
        case keepAwake
        case respectFocusModes
    }
    
    // MARK: - Initialization
    init() {
        // Defaults already set in property declarations
    }
    
    // MARK: - Codable Implementation
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        self.use24Hour = try container.decodeIfPresent(Bool.self, forKey: .use24Hour) ?? self.use24Hour
        self.showSeconds = try container.decodeIfPresent(Bool.self, forKey: .showSeconds) ?? self.showSeconds
        self.showAmPm = try container.decodeIfPresent(Bool.self, forKey: .showAmPm) ?? self.showAmPm
        self.forceHorizontalMode = try container.decodeIfPresent(Bool.self, forKey: .forceHorizontalMode) ?? self.forceHorizontalMode
        self.digitColorHex = try container.decodeIfPresent(String.self, forKey: .digitColorHex) ?? self.digitColorHex
        self.randomizeColor = try container.decodeIfPresent(Bool.self, forKey: .randomizeColor) ?? self.randomizeColor
        self.glowIntensity = try container.decodeIfPresent(Double.self, forKey: .glowIntensity) ?? self.glowIntensity
        self.digitScale = try container.decodeIfPresent(Double.self, forKey: .digitScale) ?? self.digitScale
        self.stretched = try container.decodeIfPresent(Bool.self, forKey: .stretched) ?? self.stretched
        self.backgroundHex = try container.decodeIfPresent(String.self, forKey: .backgroundHex) ?? self.backgroundHex
        self.selectedColorTheme = try container.decodeIfPresent(String.self, forKey: .selectedColorTheme) ?? self.selectedColorTheme
        self.nightModeEnabled = try container.decodeIfPresent(Bool.self, forKey: .nightModeEnabled) ?? self.nightModeEnabled
        self.autoNightMode = try container.decodeIfPresent(Bool.self, forKey: .autoNightMode) ?? self.autoNightMode
        self.scheduledNightMode = try container.decodeIfPresent(Bool.self, forKey: .scheduledNightMode) ?? self.scheduledNightMode
        self.nightModeStartTime = try container.decodeIfPresent(String.self, forKey: .nightModeStartTime) ?? self.nightModeStartTime
        self.nightModeEndTime = try container.decodeIfPresent(String.self, forKey: .nightModeEndTime) ?? self.nightModeEndTime
        self.autoBrightness = try container.decodeIfPresent(Bool.self, forKey: .autoBrightness) ?? self.autoBrightness
        self.ambientLightThreshold = try container.decodeIfPresent(Double.self, forKey: .ambientLightThreshold) ?? self.ambientLightThreshold
        // Decode font settings explicitly from strings to avoid ambiguity
        if let fontFamilyRaw = try container.decodeIfPresent(String.self, forKey: .fontFamily),
           let decoded = FontFamily(rawValue: fontFamilyRaw) {
            self.fontFamily = decoded
        }
        if let fontWeightString = try container.decodeIfPresent(String.self, forKey: .fontWeight),
           let decoded = Font.Weight(rawValue: fontWeightString) {
            self.fontWeight = decoded
        }
        if let fontDesignString = try container.decodeIfPresent(String.self, forKey: .fontDesign),
           let decoded = Font.Design(rawValue: fontDesignString) {
            self.fontDesign = decoded
        }
        self.showBattery = try container.decodeIfPresent(Bool.self, forKey: .showBattery) ?? self.showBattery
        self.showDate = try container.decodeIfPresent(Bool.self, forKey: .showDate) ?? self.showDate
        self.dateFormat = try container.decodeIfPresent(String.self, forKey: .dateFormat) ?? self.dateFormat
        self.clockOpacity = try container.decodeIfPresent(Double.self, forKey: .clockOpacity) ?? self.clockOpacity
        self.overlayOpacity = try container.decodeIfPresent(Double.self, forKey: .overlayOpacity) ?? self.overlayOpacity
        self.keepAwake = try container.decodeIfPresent(Bool.self, forKey: .keepAwake) ?? self.keepAwake
        self.respectFocusModes = try container.decodeIfPresent(Bool.self, forKey: .respectFocusModes) ?? self.respectFocusModes
        
        clearColorCache()
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(use24Hour, forKey: .use24Hour)
        try container.encode(showSeconds, forKey: .showSeconds)
        try container.encode(showAmPm, forKey: .showAmPm)
        try container.encode(forceHorizontalMode, forKey: .forceHorizontalMode)
        try container.encode(digitColorHex, forKey: .digitColorHex)
        try container.encode(randomizeColor, forKey: .randomizeColor)
        try container.encode(glowIntensity, forKey: .glowIntensity)
        try container.encode(digitScale, forKey: .digitScale)
        try container.encode(stretched, forKey: .stretched)
        try container.encode(backgroundHex, forKey: .backgroundHex)
        try container.encode(selectedColorTheme, forKey: .selectedColorTheme)
        try container.encode(nightModeEnabled, forKey: .nightModeEnabled)
        try container.encode(autoNightMode, forKey: .autoNightMode)
        try container.encode(scheduledNightMode, forKey: .scheduledNightMode)
        try container.encode(nightModeStartTime, forKey: .nightModeStartTime)
        try container.encode(nightModeEndTime, forKey: .nightModeEndTime)
        try container.encode(autoBrightness, forKey: .autoBrightness)
        try container.encode(ambientLightThreshold, forKey: .ambientLightThreshold)
        try container.encode(fontFamily.rawValue, forKey: .fontFamily)
        try container.encode(fontWeight.rawValue, forKey: .fontWeight)
        try container.encode(fontDesign.rawValue, forKey: .fontDesign)
        try container.encode(showBattery, forKey: .showBattery)
        try container.encode(showDate, forKey: .showDate)
        try container.encode(dateFormat, forKey: .dateFormat)
        try container.encode(clockOpacity, forKey: .clockOpacity)
        try container.encode(overlayOpacity, forKey: .overlayOpacity)
        try container.encode(keepAwake, forKey: .keepAwake)
        try container.encode(respectFocusModes, forKey: .respectFocusModes)
    }
    
    // MARK: - Computed Properties
    var digitColor: Color {
        if let cached = _cachedDigitColor {
            return cached
        }
        let color = Color(hex: digitColorHex) ?? .white
        _cachedDigitColor = color
        return color
    }
    
    var backgroundColor: Color {
        if let cached = _cachedBackgroundColor {
            return cached
        }
        let color = Color(hex: backgroundHex) ?? .black
        _cachedBackgroundColor = color
        return color
    }
    
    // MARK: - Helper Methods
    func clearColorCache() {
        _cachedDigitColor = nil
        _cachedBackgroundColor = nil
    }
    
    /// Apply a predefined color theme
    func applyColorTheme(_ theme: String) {
        selectedColorTheme = theme
        
        switch theme {
        case "Night":
            digitColorHex = "#FFFFFF"
            backgroundHex = "#000000"
        case "Day":
            digitColorHex = "#000000"
            backgroundHex = "#FFFFFF"
        case "Red":
            digitColorHex = "#FF3B30"
            backgroundHex = "#000000"
        case "Orange":
            digitColorHex = "#FF9500"
            backgroundHex = "#000000"
        case "Yellow":
            digitColorHex = "#FFCC00"
            backgroundHex = "#000000"
        case "Green":
            digitColorHex = "#34C759"
            backgroundHex = "#000000"
        case "Blue":
            digitColorHex = "#007AFF"
            backgroundHex = "#000000"
        case "Purple":
            digitColorHex = "#AF52DE"
            backgroundHex = "#000000"
        case "Pink":
            digitColorHex = "#FF2D92"
            backgroundHex = "#000000"
        case "White":
            digitColorHex = "#FFFFFF"
            backgroundHex = "#000000"
        default:
            // Custom theme - don't change colors
            break
        }
        
        clearColorCache()
    }
    
    /// Get available color themes
    static func availableColorThemes() -> [(String, String)] {
        return [
            ("Custom", "Custom"),
            ("Night", "Night"),
            ("Day", "Day"),
            ("Red", "Red"),
            ("Orange", "Orange"),
            ("Yellow", "Yellow"),
            ("Green", "Green"),
            ("Blue", "Blue"),
            ("Purple", "Purple"),
            ("Pink", "Pink"),
            ("White", "White")
        ]
    }
    
    /// Check if night mode should be active based on current settings
    var isNightModeActive: Bool {
        if nightModeEnabled {
            return true
        }
        
        if scheduledNightMode {
            return isWithinScheduledNightMode()
        }
        
        if autoNightMode {
            return isAmbientLightLow()
        }
        
        return false
    }
    
    /// Check if current time is within scheduled night mode hours
    private func isWithinScheduledNightMode() -> Bool {
        let now = Date()
        let calendar = Calendar.current
        let currentTime = calendar.dateComponents([.hour, .minute], from: now)
        
        let startComponents = parseTimeString(nightModeStartTime)
        let endComponents = parseTimeString(nightModeEndTime)
        
        guard let startHour = startComponents.hour,
              let startMinute = startComponents.minute,
              let endHour = endComponents.hour,
              let endMinute = endComponents.minute else {
            return false
        }
        
        let currentMinutes = (currentTime.hour ?? 0) * 60 + (currentTime.minute ?? 0)
        let startMinutes = startHour * 60 + startMinute
        let endMinutes = endHour * 60 + endMinute
        
        // Handle overnight schedules (e.g., 22:00 to 06:00)
        if startMinutes > endMinutes {
            return currentMinutes >= startMinutes || currentMinutes < endMinutes
        } else {
            return currentMinutes >= startMinutes && currentMinutes < endMinutes
        }
    }
    
    /// Parse time string in HH:mm format
    private func parseTimeString(_ timeString: String) -> (hour: Int?, minute: Int?) {
        let components = timeString.split(separator: ":")
        guard components.count == 2,
              let hour = Int(components[0]),
              let minute = Int(components[1]) else {
            return (nil, nil)
        }
        return (hour, minute)
    }
    
    /// Get the effective digit color considering night mode
    var effectiveDigitColor: Color {
        if isNightModeActive {
            return Color(hex: "#FF3B30") ?? .red // Red for night mode
        }
        return digitColor
    }
    
    /// Get the effective background color considering night mode
    var effectiveBackgroundColor: Color {
        if isNightModeActive {
            return Color(hex: "#000000") ?? .black // Black background for night mode
        }
        return backgroundColor
    }
    
    /// Check if ambient light is low enough to trigger night mode
    private func isAmbientLightLow() -> Bool {
        // Use screen brightness as a proxy for ambient light
        // In a real implementation, you'd use the ambient light sensor
        let currentBrightness = UIScreen.main.brightness
        return currentBrightness < ambientLightThreshold
    }
    
    /// Get the effective brightness considering color theme and night mode
    var effectiveBrightness: Double {
        if !autoBrightness {
            Design.debugLog("[brightness] effectiveBrightness: Auto-brightness disabled, returning 1.0")
            return 1.0 // Full brightness when auto-brightness is disabled
        }
        
        if isNightModeActive {
            Design.debugLog("[brightness] effectiveBrightness: Night mode active, returning 0.3")
            // Dim the display to 30% brightness in night mode
            return 0.3
        }
        
        // Color-aware brightness adaptation
        let colorAwareBrightness = getColorAwareBrightness()
        Design.debugLog("[brightness] effectiveBrightness: Color-aware brightness = \(String(format: "%.2f", colorAwareBrightness))")
        return colorAwareBrightness
    }
    
    /// Get brightness based on color theme and ambient light
    private func getColorAwareBrightness() -> Double {
        let baseBrightness = getBaseBrightnessForColor()
        let ambientFactor = getAmbientLightFactor()
        
        // Combine color-based brightness with ambient light factor
        return max(0.2, min(1.0, baseBrightness * ambientFactor))
    }
    
    /// Get base brightness recommendation for current color theme
    private func getBaseBrightnessForColor() -> Double {
        switch selectedColorTheme {
        case "Night":
            return 0.8
        case "Day":
            return 0.5
        case "Red", "Orange":
            return 0.6 // Warmer colors work well at lower brightness
        case "Yellow", "White":
            return 0.8 // Bright colors can be brighter
        case "Green", "Blue":
            return 0.7 // Cool colors at medium brightness
        case "Purple", "Pink":
            return 0.65 // Vibrant colors at slightly lower brightness
        default:
            // For custom colors, analyze the actual color
            return getBrightnessForCustomColor()
        }
    }
    
    /// Get brightness for custom colors based on color properties
    private func getBrightnessForCustomColor() -> Double {
        guard let color = Color(hex: digitColorHex) else { return 0.7 }
        
        // Convert to UIColor to analyze brightness
        let uiColor = UIColor(color)
        var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
        uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        
        // Calculate perceived brightness (luminance)
        let brightness = 0.299 * red + 0.587 * green + 0.114 * blue
        
        // Map brightness to recommended display brightness
        if brightness > 0.8 {
            return 0.8 // Very bright colors
        } else if brightness > 0.6 {
            return 0.7 // Medium bright colors
        } else if brightness > 0.4 {
            return 0.6 // Darker colors
        } else {
            return 0.5 // Very dark colors
        }
    }
    
    /// Get ambient light factor (0.5 to 1.0)
    private func getAmbientLightFactor() -> Double {
        let currentBrightness = UIScreen.main.brightness
        
        // Map screen brightness to ambient light factor
        if currentBrightness < 0.2 {
            return 0.5 // Very dark environment
        } else if currentBrightness < 0.4 {
            return 0.7 // Dark environment
        } else if currentBrightness < 0.6 {
            return 0.85 // Medium environment
        } else {
            return 1.0 // Bright environment
        }
    }
    
    /// Get the night mode red tint intensity
    var nightModeTintIntensity: Double {
        if isNightModeActive {
            return 0.8 // Strong red tint for night mode
        }
        return 0.0 // No tint
    }
    
    // MARK: - Equatable
    static func == (lhs: ClockStyle, rhs: ClockStyle) -> Bool {
        lhs.use24Hour == rhs.use24Hour &&
        lhs.showSeconds == rhs.showSeconds &&
        lhs.showAmPm == rhs.showAmPm &&
        lhs.forceHorizontalMode == rhs.forceHorizontalMode &&
        lhs.digitColorHex == rhs.digitColorHex &&
        lhs.randomizeColor == rhs.randomizeColor &&
        lhs.glowIntensity == rhs.glowIntensity &&
        lhs.digitScale == rhs.digitScale &&
        lhs.stretched == rhs.stretched &&
        lhs.backgroundHex == rhs.backgroundHex &&
        lhs.selectedColorTheme == rhs.selectedColorTheme &&
        lhs.nightModeEnabled == rhs.nightModeEnabled &&
        lhs.autoNightMode == rhs.autoNightMode &&
        lhs.scheduledNightMode == rhs.scheduledNightMode &&
        lhs.nightModeStartTime == rhs.nightModeStartTime &&
        lhs.nightModeEndTime == rhs.nightModeEndTime &&
        lhs.autoBrightness == rhs.autoBrightness &&
        lhs.ambientLightThreshold == rhs.ambientLightThreshold &&
        lhs.fontFamily == rhs.fontFamily &&
        lhs.fontWeight == rhs.fontWeight &&
        lhs.fontDesign == rhs.fontDesign &&
        lhs.showBattery == rhs.showBattery &&
        lhs.showDate == rhs.showDate &&
        lhs.dateFormat == rhs.dateFormat &&
        lhs.clockOpacity == rhs.clockOpacity &&
        lhs.overlayOpacity == rhs.overlayOpacity &&
        lhs.keepAwake == rhs.keepAwake &&
        lhs.respectFocusModes == rhs.respectFocusModes
    }
}

// MARK: - Storage Key
extension ClockStyle {
    static let appStorageKey = AppConstants.StorageKeys.clockStyle
}
