//
//  UserDefaultsStore.swift
//  Reflect (iOS)
//
//  Created by Trey Tartt on 1/22/22.
//

import Foundation

enum VotingLayoutStyle: Int, CaseIterable {
    case horizontal = 0   // Current: 5 buttons in a row
    case cards = 1        // Larger tappable cards with labels
    case stacked = 2      // Full-width vertical list
    case aura = 3         // Atmospheric glowing orbs with flowing layout
    case orbit = 4        // Celestial orbit with center core
    case neon = 5         // Synthwave arcade equalizer with glowing segments

    var displayName: String {
        switch self {
        case .horizontal: return "Horizontal"
        case .cards: return "Cards"
        case .stacked: return "Stacked"
        case .aura: return "Aura"
        case .orbit: return "Orbit"
        case .neon: return "Neon"
        }
    }
}

enum PaywallStyle: Int, CaseIterable {
    case celestial = 0    // Celestial Self-Discovery - aurora, floating orbs
    case garden = 1       // Garden Growth - organic, blooming nature
    case neon = 2         // Neon Pulse - synthwave, energetic
    case minimal = 3      // Minimal Zen - clean, sophisticated
    case zen = 4          // Zen Garden - ink brushstrokes, meditation
    case editorial = 5    // Editorial - magazine typography
    case mixtape = 6      // Mixtape - cassette/retro analog
    case heartfelt = 7    // Heartfelt - hearts and emotion
    case luxe = 8         // Luxe - premium glass materials
    case forecast = 9     // Forecast - weather metaphors
    case playful = 10     // Playful - vibrant emoji patterns
    case journal = 11     // Journal - handwritten paper

    var displayName: String {
        switch self {
        case .celestial: return "Celestial"
        case .garden: return "Garden"
        case .neon: return "Neon"
        case .minimal: return "Minimal"
        case .zen: return "Zen"
        case .editorial: return "Editorial"
        case .mixtape: return "Mixtape"
        case .heartfelt: return "Heartfelt"
        case .luxe: return "Luxe"
        case .forecast: return "Forecast"
        case .playful: return "Playful"
        case .journal: return "Journal"
        }
    }

    var description: String {
        switch self {
        case .celestial: return "Aurora lights & floating emotion orbs"
        case .garden: return "Blooming flowers & organic growth"
        case .neon: return "Synthwave energy & glowing pulses"
        case .minimal: return "Clean typography & subtle elegance"
        case .zen: return "Ink brushstrokes & meditative calm"
        case .editorial: return "Magazine typography & literary elegance"
        case .mixtape: return "Cassette tapes & analog nostalgia"
        case .heartfelt: return "Hearts & emotional expression"
        case .luxe: return "Premium glass & refined materials"
        case .forecast: return "Weather metaphors & natural flow"
        case .playful: return "Vibrant patterns & playful energy"
        case .journal: return "Handwritten notes & paper textures"
        }
    }
}

enum LockScreenStyle: Int, CaseIterable {
    case aurora = 0       // Default - emotional aurora with breathing orb
    case zen = 1          // Zen - ink circles, minimal, calming
    case neon = 2         // Neon - synthwave grid, glowing elements
    case celestial = 3    // Celestial - stars, moon phases, cosmic
    case editorial = 4    // Editorial - typography focused, clean
    case mixtape = 5      // Mixtape - cassette aesthetic, retro
    case bloom = 6        // Bloom - organic shapes, flowers
    case heartfelt = 7    // Heartfelt - hearts, soft gradients
    case minimal = 8      // Minimal - ultra clean, simple
    case luxe = 9         // Luxe - glass, premium materials
    case forecast = 10    // Forecast - weather, atmospheric
    case playful = 11     // Playful - patterns, vibrant
    case journal = 12     // Journal - paper, handwritten feel

    var displayName: String {
        switch self {
        case .aurora: return "Aurora"
        case .zen: return "Zen"
        case .neon: return "Neon"
        case .celestial: return "Celestial"
        case .editorial: return "Editorial"
        case .mixtape: return "Mixtape"
        case .bloom: return "Bloom"
        case .heartfelt: return "Heartfelt"
        case .minimal: return "Minimal"
        case .luxe: return "Luxe"
        case .forecast: return "Forecast"
        case .playful: return "Playful"
        case .journal: return "Journal"
        }
    }
}

enum DayViewStyle: Int, CaseIterable {
    case classic = 0      // Current card style with gradient icons
    case minimal = 1      // Clean, simple flat cards
    case compact = 2      // Dense timeline view
    case bubble = 3       // Colorful full-width bubbles
    case grid = 4         // 3 entries per row grid
    case aura = 5         // Atmospheric glowing entries with giant typography
    case chronicle = 6    // Editorial magazine with dramatic serif typography
    case neon = 7         // Cyberpunk synthwave with glowing edges
    case ink = 8          // Japanese zen calligraphy with brush strokes
    case prism = 9        // Premium glassmorphism with light refraction
    case tape = 10        // Retro cassette mixtape aesthetic
    case morph = 11       // Liquid organic blob shapes
    case stack = 12       // Layered paper notes with depth
    case wave = 13        // Horizontal gradient river bands
    case pattern = 14     // Mood icons as repeating background pattern
    case leather = 15     // Skeuomorphic leather with stitching
    case glass = 16       // Liquid glass with variable blur
    case motion = 17      // Accelerometer-driven parallax effect
    case micro = 18       // Ultra compact single-line entries
    case orbit = 19       // Celestial circular orbital arrangement

    var displayName: String {
        switch self {
        case .classic: return "Classic"
        case .minimal: return "Minimal"
        case .compact: return "Compact"
        case .bubble: return "Bubble"
        case .grid: return "Grid"
        case .aura: return "Aura"
        case .chronicle: return "Chronicle"
        case .neon: return "Neon"
        case .ink: return "Ink"
        case .prism: return "Prism"
        case .tape: return "Tape"
        case .morph: return "Morph"
        case .stack: return "Stack"
        case .wave: return "Wave"
        case .pattern: return "Pattern"
        case .leather: return "Leather"
        case .glass: return "Glass"
        case .motion: return "Motion"
        case .micro: return "Micro"
        case .orbit: return "Orbit"
        }
    }

    var isGridLayout: Bool {
        self == .grid
    }

    /// Styles available in the picker (some are disabled/experimental)
    var isAvailable: Bool {
        switch self {
        case .motion, .leather, .wave, .morph, .prism, .ink:
            return false
        default:
            return true
        }
    }

    /// All styles available to users
    static var availableCases: [DayViewStyle] {
        allCases.filter { $0.isAvailable }
    }
}

class UserDefaultsStore {
    enum Keys: String {
        case savedOnboardingData
        case needsOnboarding
        case useCloudKit
        case deleteEnable
        case mainViewTopHeaderIndex
        case theme
        case moodImages
        case moodTint
        case personalityPack
        case customWidget
        case customMoodTint
        case customMoodTintUpdateNumber
        case showNSFW
        case shape
        case daysFilter
        case firstLaunchDate
        case hasActiveSubscription
        case cachedSubscriptionExpiration
        case lastVotedDate
        case votingLayoutStyle
        case dayViewStyle
        case privacyLockEnabled
        case healthKitEnabled
        case healthKitSyncEnabled
        case paywallStyle
        case lockScreenStyle
        case celebrationAnimation
        case hapticFeedbackEnabled

        case contentViewCurrentSelectedHeaderViewBackDays
        case contentViewHeaderTag
        case contentViewHeaderTagViewOneViewType
        case contentViewHeaderTagViewTwoViewType
        case currentSelectedHeaderViewViewType
    }
    
    /// Cached onboarding data to avoid repeated JSON decoding
    private static var cachedOnboardingData: OnboardingData?

    static func getOnboarding() -> OnboardingData {
        // Return cached data if available
        if let cached = cachedOnboardingData {
            return cached
        }

        // Decode and cache
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.savedOnboardingData.rawValue) as? Data,
           let model = try? JSONDecoder().decode(OnboardingData.self, from: data) {
            cachedOnboardingData = model
            return model
        } else {
            let defaultData = OnboardingData()
            cachedOnboardingData = defaultData
            return defaultData
        }
    }

    /// Invalidate cached onboarding data (call when data might have changed externally)
    static func invalidateOnboardingCache() {
        cachedOnboardingData = nil
    }

    @discardableResult
    static func saveOnboarding(onboardingData: OnboardingData) -> OnboardingData {
        // Invalidate cache before saving
        cachedOnboardingData = nil

        do {
            let data = try JSONEncoder().encode(onboardingData)
            GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.savedOnboardingData.rawValue)
        } catch {
            print("Error saving onboarding: \(error)")
        }

        // Re-cache the saved data
        cachedOnboardingData = onboardingData
        return onboardingData
    }
    
    static func moodMoodImagable() -> MoodImagable.Type {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.moodImages.rawValue) as? Int,
           let model = MoodImages.init(rawValue: data) {
            return model.moodImages
        } else {
            return MoodImages.FontAwesome.moodImages
        }
    }
    
    static func moodTintable() -> MoodTintable.Type {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.moodTint.rawValue) as? Int,
           let model = MoodTints.init(rawValue: data) {
            return model.moodTints
        } else {
            return MoodTints.Default.moodTints
        }
    }
    
    static func personalityPackable() -> PersonalityPack {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.personalityPack.rawValue) as? Int,
           let model = PersonalityPack.init(rawValue: data) {
            return model
        } else {
            return PersonalityPack.Default
        }
    }
    
    static func theme() -> Theme {
        // Try String value first (new format)
        if let stringValue = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.theme.rawValue) as? String,
           let model = Theme(rawValue: stringValue) {
            return model
        }
        // Fall back to Int value (legacy format for existing users)
        if let intValue = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.theme.rawValue) as? Int,
           let model = Theme(legacyIntValue: intValue) {
            // Migrate to new String format
            GroupUserDefaults.groupDefaults.set(model.rawValue, forKey: UserDefaultsStore.Keys.theme.rawValue)
            return model
        }
        return Theme.system
    }

    /// Call this on app launch to migrate any legacy Int-based theme values to String format
    static func migrateThemeIfNeeded() {
        // Check if we have an Int value stored (legacy format)
        if let intValue = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.theme.rawValue) as? Int,
           let model = Theme(legacyIntValue: intValue) {
            // Migrate to new String format
            GroupUserDefaults.groupDefaults.set(model.rawValue, forKey: UserDefaultsStore.Keys.theme.rawValue)
            GroupUserDefaults.groupDefaults.synchronize()
        }
    }
    
    static func getCustomWidgets() -> [CustomWidgetModel] {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.customWidget.rawValue) as? Data,
           let model = try? JSONDecoder().decode([CustomWidgetModel].self, from: data) {
            return model
        } else {
            GroupUserDefaults.groupDefaults.removeObject(forKey: UserDefaultsStore.Keys.customWidget.rawValue)

            let widget = CustomWidgetModel.randomWidget
            widget.isSaved = true
            let widgets = [widget]

            guard let data = try? JSONEncoder().encode(widgets) else {
                return widgets
            }
            GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.customWidget.rawValue)

            if let savedData = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.customWidget.rawValue) as? Data,
               let models = try? JSONDecoder().decode([CustomWidgetModel].self, from: savedData) {
                return models.sorted { $0.createdDate < $1.createdDate }
            } else {
                return widgets
            }
        }
    }
    
    @discardableResult
    static func saveCustomWidget(widgetModel: CustomWidgetModel, inUse: Bool) -> [CustomWidgetModel] {
        do {
            var existingWidgets = getCustomWidgets()
            
            if let exisitingWidget = existingWidgets.firstIndex(where: {
                $0.uuid == widgetModel.uuid
            }) {
                existingWidgets.remove(at: exisitingWidget)
                // give it differnet uuid so the view updates
                widgetModel.uuid = UUID().uuidString
            }
            
            if inUse {
                existingWidgets.forEach({
                    $0.inUse = false
                })
                
                widgetModel.inUse = true
            }
    
            existingWidgets.append(widgetModel)
            existingWidgets.forEach({
                $0.isSaved = true
            })
            let data = try JSONEncoder().encode(existingWidgets)
            GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.customWidget.rawValue)
        } catch {
            print("Error saving custom widget: \(error)")
        }
        return UserDefaultsStore.getCustomWidgets()
    }

    @discardableResult
    static func deleteCustomWidget(withUUID uuid: String) -> [CustomWidgetModel] {
        do {
            var existingWidgets = getCustomWidgets()
            
            if let exisitingWidget = existingWidgets.firstIndex(where: {
                $0.uuid == uuid
            }) {
                existingWidgets.remove(at: exisitingWidget)
            }
            
            if existingWidgets.count == 0 {
                let widget = CustomWidgetModel.randomWidget
                widget.isSaved = true
                widget.inUse = true
                existingWidgets.append(widget)
            }
            
            if existingWidgets.first(where: { $0.inUse == true }) == nil {
                existingWidgets.first?.inUse = true
            }

            let data = try JSONEncoder().encode(existingWidgets)
            GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.customWidget.rawValue)
        } catch {
            print("Error deleting custom widget: \(error)")
        }
        return UserDefaultsStore.getCustomWidgets()
    }

    static func getCustomMoodTint() -> SavedMoodTint {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.customMoodTint.rawValue) as? Data{
            do {
                let model = try JSONDecoder().decode(SavedMoodTint.self, from: data)
                return model
            } catch {
                print(error)
            }
        }
        return SavedMoodTint()
    }
    
    static func getCustomBGShape() -> BGShape {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.shape.rawValue) as? Int,
           let model = BGShape.init(rawValue: data) {
            return model
        } else {
            return BGShape.circle
        }
    }
    
    @discardableResult
    static func saveCustomMoodTint(customTint: SavedMoodTint) -> SavedMoodTint {
        do {
            let data = try JSONEncoder().encode(customTint)
            GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.customMoodTint.rawValue)
        } catch {
            print("Error saving custom mood tint: \(error)")
        }
        return UserDefaultsStore.getCustomMoodTint()
    }
    
    @discardableResult
    static func saveDaysFilter(days: [Int]) -> [Int] {
        GroupUserDefaults.groupDefaults.set(days, forKey: UserDefaultsStore.Keys.daysFilter.rawValue)
        return UserDefaultsStore.getDaysFilter()
    }
    
    static func getDaysFilter() -> [Int] {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.daysFilter.rawValue) as? [Int] {
            return data
        } else {
            return [1,2,3,4,5,6,7]
        }
    }
}

