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

import SwiftUI
import CoreData

class ContentModeViewModel: ObservableObject {
    @Published var grouped = [Int: [Int: [MoodEntry]]]()
    @Published public private(set) var savedOnboardingData = UserDefaultsStore.getOnboarding()
    @Published var numberOfItems = 0
    
    var hasNoData: Bool {
        grouped.isEmpty
    }
    
    private var numberOfEntries: Int {
        var num = 0
        grouped.keys.forEach({
            let year = grouped[$0]
            let monthKeys = year?.keys
            monthKeys?.forEach({
                num += year![$0]!.count
            })
        })
        return num
        
//        grouped.keys.map{
//            grouped[$0]!.values.reduce(0) { sum, array in
//                sum + array.count
//            }
//        }.reduce(0, +)
    }
    
    init() {
        PersistenceController.shared.switchContainerListeners.append {
            self.getGroupedData()
            
        }
        updateData()
    }
    
    private func getGroupedData() {
        grouped = PersistenceController.shared.splitIntoYearMonth()
        numberOfItems = numberOfEntries
    }
    
    public func shouldShowVotingHeader() -> Bool {
        isMissingCurrentVote() && savedOnboardingData.ableToVoteBasedOnCurentTime() ? true : false
    }
    
    public func updateOnboardingData(onboardingData: OnboardingData) {
        self.savedOnboardingData = UserDefaultsStore.saveOnboarding(onboardingData: onboardingData)
        LocalNotification.scheduleReminder(atTime: onboardingData.date, withTitle: onboardingData.title)
    }
    
    private func isMissingCurrentVote() -> Bool {
        let fetchRequest = NSFetchRequest<MoodEntry>(entityName: "MoodEntry")
        
        var calendar = Calendar.current
        calendar.timeZone = NSTimeZone.local
        
        var dateFrom: Date
        switch savedOnboardingData.inputDay {
        case .Today:
            dateFrom = calendar.startOfDay(for: Date())
        case .Previous:
            dateFrom = calendar.startOfDay(for: Date())
            dateFrom = calendar.date(byAdding: .day, value: -1, to: dateFrom)!
        }
        let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)!
        
        let fromPredicate = NSPredicate(format: "%@ <= %K", dateFrom as NSDate, #keyPath(MoodEntry.forDate))
        let toPredicate = NSPredicate(format: "%K < %@", #keyPath(MoodEntry.forDate), dateTo as NSDate)
        let datePredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fromPredicate, toPredicate])
        fetchRequest.predicate = datePredicate
        let entries = try! PersistenceController.shared.viewContext.count(for: fetchRequest)
        
        return entries == 0
    }
    
    public func updateData() {
        getGroupedData()
    }
    
    public func add(mood: Mood, forDate date: Date) {
        PersistenceController.shared.add(mood: mood, forDate: date)
        getGroupedData()
    }
    
    public func update(entry: MoodEntry, toMood mood: Mood) {
        let forDate = entry.forDate!
        
        if let entry = PersistenceController.shared.getEntry(byDate: entry.forDate!) {
            PersistenceController.shared.viewContext.delete(entry)
        }
        
        do {
            try PersistenceController.shared.viewContext.save()
            getGroupedData()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        
        PersistenceController.shared.add(mood: mood, forDate: forDate)
        
        do {
            try PersistenceController.shared.viewContext.save()
            getGroupedData()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
    
    public func delete(offsets: IndexSet, inMonth month: Int, inYear year: Int) {
        if let monthEntries = grouped[year],
           let entries = monthEntries[month] {
            var mutableEntries = entries.sorted(by: {
                $0.forDate! > $1.forDate!
            })
            var entriesToDelete = [MoodEntry]()
            for idx in offsets {
                let obj = mutableEntries.remove(at: idx)
                entriesToDelete.append(obj)
            }
            entriesToDelete.forEach({ entry in
                let entryDate = entry.forDate!
                PersistenceController.shared.viewContext.delete(entry)
                self.add(mood: .missing, forDate: entryDate)
            })
        }
        
        do {
            try PersistenceController.shared.viewContext.save()
            getGroupedData()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}
