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

import Foundation

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 textColor
        case showNSFW
        case shape
        case daysFilter
        
        case contentViewCurrentSelectedHeaderViewBackDays
        case contentViewHeaderTag
        case contentViewHeaderTagViewOneViewType
        case contentViewHeaderTagViewTwoViewType
        case currentSelectedHeaderViewViewType
    }
    
    static func getOnboarding() -> OnboardingData {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.savedOnboardingData.rawValue) as? Data,
           let model = try? JSONDecoder().decode(OnboardingData.self, from: data) {
            return model
        } else {
            return OnboardingData()
        }
    }
    
    static func saveOnboarding(onboardingData: OnboardingData) -> OnboardingData {
        do {
            let data = try JSONEncoder().encode(onboardingData)
            GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.savedOnboardingData.rawValue)
            return UserDefaultsStore.getOnboarding()
        } catch {
            fatalError("error saving")
        }
    }
    
    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 {
        if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.theme.rawValue) as? Int,
           let model = Theme.init(rawValue: data) {
            return model
        } else {
            return Theme.system
        }
    }
    
    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]
            let data = try! JSONEncoder().encode(widgets)
            GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.customWidget.rawValue)

            if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.customWidget.rawValue) as? Data,
               let models = try? JSONDecoder().decode([CustomWidgetModel].self, from: data) {
                let sorted = models.sorted(by: {
                    $0.createdDate < $1.createdDate
                })
                return sorted
            } else {
                fatalError("error getting 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)
            return UserDefaultsStore.getCustomWidgets()
        } catch {
            fatalError("error saving")
        }
    }
    
    @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 let _ = existingWidgets.first(where: {
                $0.inUse == true
            }) {} else {
                if let first = existingWidgets.first {
                    first.inUse = true
                }
            }
            
            let data = try JSONEncoder().encode(existingWidgets)
            GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.customWidget.rawValue)
            return UserDefaultsStore.getCustomWidgets()
        } catch {
            fatalError("error saving")
        }
    }
    
    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)
            return UserDefaultsStore.getCustomMoodTint()
        } catch {
            print(error)
            fatalError("error saving")
        }
    }
    
    @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]
        }
    }
}

