//
//  WidgetProviders.swift
//  ReflectWidget
//
//  Timeline providers for widget data
//

import WidgetKit
import SwiftUI
import Intents

// MARK: - Timeline Creator

struct TimeLineCreator {
    @MainActor static func createViews(daysBack: Int) -> [WatchTimelineView] {
        var timeLineView = [WatchTimelineView]()

        let latestDayToShow = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
        let dates = Array(0...daysBack).map({
           Calendar.current.date(byAdding: .day, value: -$0, to: latestDayToShow)!
        })

        // Use WidgetDataProvider for isolated widget data access
        let dataProvider = WidgetDataProvider.shared

        for date in dates {
            let dayStart = Calendar.current.startOfDay(for: date)
            let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!
            let moodTint: MoodTintable.Type = UserDefaultsStore.moodTintable()
            let moodImages: MoodImagable.Type = UserDefaultsStore.moodMoodImagable()

            if let todayEntry = dataProvider.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first {
                timeLineView.append(WatchTimelineView(image: moodImages.icon(forMood: todayEntry.mood),
                                                      graphic: moodImages.icon(forMood: todayEntry.mood),
                                                      date: dayStart,
                                                      color: moodTint.color(forMood: todayEntry.mood),
                                                      secondaryColor: moodTint.secondary(forMood: todayEntry.mood),
                                                      mood: todayEntry.mood))
            } else {
                timeLineView.append(WatchTimelineView(image: moodImages.icon(forMood: .missing),
                                                      graphic: moodImages.icon(forMood: .missing),
                                                      date: dayStart,
                                                      color: moodTint.color(forMood: .missing),
                                                      secondaryColor: moodTint.secondary(forMood: .missing),
                                                      mood: .missing))
            }
        }

        timeLineView = timeLineView.sorted(by: { $0.date > $1.date  })
        return timeLineView
    }

    /// Creates sample preview data for widget picker - shows what widget looks like with mood data
    static func createSampleViews(count: Int) -> [WatchTimelineView] {
        var timeLineView = [WatchTimelineView]()
        let sampleMoods: [Mood] = [.great, .good, .average, .good, .great, .average, .bad, .good, .great, .good, .average]
        let moodTint: MoodTintable.Type = UserDefaultsStore.moodTintable()
        let moodImages: MoodImagable.Type = UserDefaultsStore.moodMoodImagable()

        for i in 0..<count {
            let date = Calendar.current.date(byAdding: .day, value: -i, to: Date())!
            let dayStart = Calendar.current.startOfDay(for: date)
            let mood = sampleMoods[i % sampleMoods.count]

            timeLineView.append(WatchTimelineView(
                image: moodImages.icon(forMood: mood),
                graphic: moodImages.icon(forMood: mood),
                date: dayStart,
                color: moodTint.color(forMood: mood),
                secondaryColor: moodTint.secondary(forMood: mood),
                mood: mood
            ))
        }

        return timeLineView
    }
}

// MARK: - Timeline Widget Provider

struct Provider: @preconcurrency IntentTimelineProvider {
    typealias Entry = SimpleEntry
    typealias Intent = ConfigurationIntent

    let timeLineCreator = TimeLineCreator()

    func placeholder(in context: Context) -> SimpleEntry {
        return SimpleEntry(date: Date(),
                           configuration: ConfigurationIntent(),
                           timeLineViews: TimeLineCreator.createSampleViews(count: 10))
    }

    @MainActor func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        // Use sample data for widget picker preview, real data otherwise
        let timeLineViews: [WatchTimelineView]
        if context.isPreview {
            timeLineViews = TimeLineCreator.createSampleViews(count: 10)
        } else {
            timeLineViews = Array(TimeLineCreator.createViews(daysBack: 11).prefix(10))
        }
        let (hasSubscription, hasVotedToday, promptText) = checkSubscriptionAndVoteStatus()
        let entry = SimpleEntry(date: Date(),
                                configuration: ConfigurationIntent(),
                                timeLineViews: timeLineViews,
                                hasSubscription: hasSubscription,
                                hasVotedToday: hasVotedToday,
                                promptText: promptText)
        completion(entry)
    }

    @MainActor func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let (hasSubscription, hasVotedToday, promptText) = checkSubscriptionAndVoteStatus()
        let entry = SimpleEntry(date: Calendar.current.date(byAdding: .second, value: 15, to: Date())!,
                                configuration: ConfigurationIntent(),
                                timeLineViews: nil,
                                hasSubscription: hasSubscription,
                                hasVotedToday: hasVotedToday,
                                promptText: promptText)

        let midNightEntry = SimpleEntry(date: Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())!,
                                        configuration: ConfigurationIntent(),
                                        timeLineViews: nil,
                                        hasSubscription: hasSubscription,
                                        hasVotedToday: hasVotedToday,
                                        promptText: promptText)

        let date = Calendar.current.date(byAdding: .second, value: 10, to: Date())!
        let timeline = Timeline(entries: [entry, midNightEntry], policy: .after(date))
        completion(timeline)
    }

    @MainActor
    private func checkSubscriptionAndVoteStatus() -> (hasSubscription: Bool, hasVotedToday: Bool, promptText: String) {
        let hasSubscription = GroupUserDefaults.groupDefaults.bool(forKey: UserDefaultsStore.Keys.hasActiveSubscription.rawValue)

        let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
        let dayStart = Calendar.current.startOfDay(for: votingDate)
        let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart) ?? dayStart

        // Use WidgetDataProvider for isolated widget data access
        let todayEntry = WidgetDataProvider.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
        let hasVotedToday = todayEntry != nil && todayEntry?.mood != Mood.missing && todayEntry?.mood != Mood.placeholder

        let promptText = UserDefaultsStore.personalityPackable().randomPushNotificationStrings().body

        return (hasSubscription, hasVotedToday, promptText)
    }
}

// MARK: - Vote Widget Provider

struct VoteWidgetProvider: TimelineProvider {
    func placeholder(in context: Context) -> VoteWidgetEntry {
        // Show sample "already voted" state for widget picker preview
        let sampleStats = MoodStats(totalEntries: 30, moodCounts: [.great: 10, .good: 12, .average: 5, .bad: 2, .horrible: 1])
        let promptText = UserDefaultsStore.personalityPackable().randomPushNotificationStrings().body
        return VoteWidgetEntry(date: Date(), votingDate: Date(), hasSubscription: true, hasVotedToday: true, todaysMood: .good, stats: sampleStats, promptText: promptText)
    }

    func getSnapshot(in context: Context, completion: @escaping (VoteWidgetEntry) -> Void) {
        // Show sample data for widget picker preview
        if context.isPreview {
            let sampleStats = MoodStats(totalEntries: 30, moodCounts: [.great: 10, .good: 12, .average: 5, .bad: 2, .horrible: 1])
            let promptText = UserDefaultsStore.personalityPackable().randomPushNotificationStrings().body
            let entry = VoteWidgetEntry(date: Date(), votingDate: Date(), hasSubscription: true, hasVotedToday: true, todaysMood: .good, stats: sampleStats, promptText: promptText)
            completion(entry)
            return
        }
        Task { @MainActor in
            let entry = createEntry()
            completion(entry)
        }
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<VoteWidgetEntry>) -> Void) {
        Task { @MainActor in
            let entry = createEntry()

            // Calculate next refresh time
            let nextRefresh = calculateNextRefreshDate()
            let timeline = Timeline(entries: [entry], policy: .after(nextRefresh))
            completion(timeline)
        }
    }

    /// Calculate when the widget should next refresh
    /// Refreshes at: rating time (to show voting view) and midnight (for new day)
    private func calculateNextRefreshDate() -> Date {
        let now = Date()
        let calendar = Calendar.current

        // Get the rating time from onboarding data
        let onboardingData = UserDefaultsStore.getOnboarding()
        let ratingTimeComponents = calendar.dateComponents([.hour, .minute], from: onboardingData.date)

        // Create today's rating time
        var todayRatingComponents = calendar.dateComponents([.year, .month, .day], from: now)
        todayRatingComponents.hour = ratingTimeComponents.hour
        todayRatingComponents.minute = ratingTimeComponents.minute
        let todayRatingTime = calendar.date(from: todayRatingComponents) ?? now

        // Tomorrow's midnight
        let midnight = calendar.startOfDay(for: calendar.date(byAdding: .day, value: 1, to: now)!)

        // If we haven't passed today's rating time, refresh at rating time
        if now < todayRatingTime {
            return todayRatingTime
        }

        // Otherwise refresh at midnight
        return midnight
    }

    @MainActor
    private func createEntry() -> VoteWidgetEntry {
        let hasSubscription = GroupUserDefaults.groupDefaults.bool(forKey: UserDefaultsStore.Keys.hasActiveSubscription.rawValue)

        // Use WidgetDataProvider for isolated read-only data access
        let dataProvider = WidgetDataProvider.shared

        let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
        let dayStart = Calendar.current.startOfDay(for: votingDate)
        let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!

        // Check if user has voted today
        let todayEntry = dataProvider.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
        let hasVotedToday = todayEntry != nil && todayEntry?.mood != Mood.missing && todayEntry?.mood != Mood.placeholder

        // Get today's mood if voted
        let todaysMood: Mood? = hasVotedToday ? todayEntry?.mood : nil

        // Get stats for display after voting
        var stats: MoodStats? = nil
        if hasVotedToday {
            let allEntries = dataProvider.getData(
                startDate: Date(timeIntervalSince1970: 0),
                endDate: Date(),
                includedDays: []
            )
            let validEntries = allEntries.filter { $0.mood != Mood.missing && $0.mood != Mood.placeholder }
            let totalCount = validEntries.count

            if totalCount > 0 {
                var moodCounts: [Mood: Int] = [:]
                for entry in validEntries {
                    moodCounts[entry.mood, default: 0] += 1
                }
                stats = MoodStats(totalEntries: totalCount, moodCounts: moodCounts)
            }
        }

        // Get random prompt text for voting view
        let promptText = UserDefaultsStore.personalityPackable().randomPushNotificationStrings().body

        return VoteWidgetEntry(
            date: Date(),
            votingDate: votingDate,
            hasSubscription: hasSubscription,
            hasVotedToday: hasVotedToday,
            todaysMood: todaysMood,
            stats: stats,
            promptText: promptText
        )
    }
}
