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

import SwiftUI
import SwiftData

@MainActor
class DayViewViewModel: ObservableObject {
    @Published var grouped = [Int: [Int: [MoodEntryModel]]]()
    @Published var numberOfItems = 0
    @Published var sortedGroupedData: [(year: Int, months: [(month: Int, entries: [MoodEntryModel])])] = []

    let addMonthStartWeekdayPadding: Bool

    var hasNoData: Bool {
        grouped.isEmpty
    }

    private var numberOfEntries: Int {
        Self.countEntries(in: grouped)
    }

    /// Count total entries across all year/month groups. Extracted for testability.
    static func countEntries(in grouped: [Int: [Int: [MoodEntryModel]]]) -> Int {
        grouped.values.flatMap(\.values).reduce(0) { $0 + $1.count }
    }

    private var dataListenerToken: DataController.DataListenerToken?

    init(addMonthStartWeekdayPadding: Bool) {
        self.addMonthStartWeekdayPadding = addMonthStartWeekdayPadding

        dataListenerToken = DataController.shared.addNewDataListener { [weak self] in
            guard let self = self else { return }
            // Avoid withAnimation for bulk data updates - it causes expensive view diffing
            self.updateData()
        }
        updateData()
    }

    deinit {
        if let token = dataListenerToken {
            Task { @MainActor in
                DataController.shared.removeDataListener(token: token)
            }
        }
    }

    private func getGroupedData(addMonthStartWeekdayPadding: Bool) {
        var newStuff = DataController.shared.splitIntoYearMonth(includedDays: [1,2,3,4,5,6,7])
        if addMonthStartWeekdayPadding {
            newStuff = MoodEntryFunctions.padMoodEntriesForCalendar(entries: newStuff)
        }
        grouped = newStuff
        numberOfItems = numberOfEntries
    }

    public func updateData() {
        getGroupedData(addMonthStartWeekdayPadding: self.addMonthStartWeekdayPadding)
        sortedGroupedData = grouped
            .sorted { $0.key > $1.key }
            .map { (year: $0.key, months: $0.value.sorted { $0.key > $1.key }.map { (month: $0.key, entries: $0.value) }) }
    }

    public func add(mood: Mood, forDate date: Date, entryType: EntryType) {
        MoodLogger.shared.logMood(mood, for: date, entryType: entryType)
    }

    public func update(entry: MoodEntryModel, toMood mood: Mood) {
        if !MoodLogger.shared.updateMood(entryDate: entry.forDate, withMood: mood) {
            print("Failed to update mood entry")
        }
    }

    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 = [MoodEntryModel]()
            for idx in offsets {
                let obj = mutableEntries.remove(at: idx)
                entriesToDelete.append(obj)
            }
            entriesToDelete.forEach({ entry in
                MoodLogger.shared.deleteMood(forDate: entry.forDate)
            })
        }
    }

    static func updateTitleHeader(forEntry entry: MoodEntryModel?) -> String {
        guard let entry = entry else {
            return ""
        }

        let forDate = entry.forDate

        let components = Calendar.current.dateComponents([.day, .month, .year], from: forDate)
        let month = components.month ?? 1
        let year = components.year ?? Calendar.current.component(.year, from: Date())

        let monthName = Random.monthName(fromMonthInt: month)
        let weekday = Random.weekdayName(fromDate: entry.forDate)
        let dayz = Random.dayFormat(fromDate: entry.forDate)

        let string = weekday + " " + monthName + " " + dayz + " " + String(year)

        return String(format: String(localized: "content_view_fill_in_missing_entry"), string)
    }
}
