import Foundation
import SharedModels
import SwiftData

enum Badge: String, CaseIterable, Identifiable, Sendable {
    case firstReview
    case streak3
    case streak7
    case streak30
    case verbs25
    case verbs100
    case presentMaster = "present_master"
    case preteriteMaster = "preterite_master"
    case allTenses
    case daily50

    var id: String { rawValue }

    var title: String {
        switch self {
        case .firstReview: "First Steps"
        case .streak3: "Three in a Row"
        case .streak7: "Weekly Warrior"
        case .streak30: "Monthly Master"
        case .verbs25: "Quarter Century"
        case .verbs100: "Century Club"
        case .presentMaster: "Present Tense Pro"
        case .preteriteMaster: "Past Perfect"
        case .allTenses: "Tense Champion"
        case .daily50: "Daily Dedication"
        }
    }

    var description: String {
        switch self {
        case .firstReview: "Complete your first review"
        case .streak3: "3 day streak"
        case .streak7: "7 day streak"
        case .streak30: "30 day streak"
        case .verbs25: "Review 25 unique verbs"
        case .verbs100: "Review 100 unique verbs"
        case .presentMaster: "Master all basic present tense verbs"
        case .preteriteMaster: "Master all basic preterite verbs"
        case .allTenses: "Practice all 20 tenses"
        case .daily50: "Complete 50 reviews in one day"
        }
    }

    var icon: String {
        switch self {
        case .firstReview: "star.fill"
        case .streak3: "flame.fill"
        case .streak7: "flame.fill"
        case .streak30: "flame.circle.fill"
        case .verbs25: "25.square.fill"
        case .verbs100: "100.circle.fill"
        case .presentMaster: "checkmark.seal.fill"
        case .preteriteMaster: "checkmark.seal.fill"
        case .allTenses: "trophy.fill"
        case .daily50: "bolt.fill"
        }
    }
}

struct AchievementService: Sendable {

    /// Check all badges and return any newly earned ones.
    static func checkAchievements(
        progress: UserProgress,
        reviewContext: ModelContext,
        referenceContext: ModelContext
    ) -> [Badge] {
        var newBadges: [Badge] = []

        for badge in Badge.allCases {
            guard !progress.unlockedBadgeIDs.contains(badge.rawValue) else { continue }

            let earned: Bool
            switch badge {
            case .firstReview:
                earned = progress.totalReviewed >= 1
            case .streak3:
                earned = progress.currentStreak >= 3
            case .streak7:
                earned = progress.currentStreak >= 7
            case .streak30:
                earned = progress.currentStreak >= 30
            case .verbs25:
                earned = uniqueVerbsReviewed(context: reviewContext) >= 25
            case .verbs100:
                earned = uniqueVerbsReviewed(context: reviewContext) >= 100
            case .presentMaster:
                earned = hasMasteredTense(
                    TenseID.ind_presente.rawValue,
                    level: VerbLevel.basic.rawValue,
                    reviewContext: reviewContext,
                    referenceContext: referenceContext
                )
            case .preteriteMaster:
                earned = hasMasteredTense(
                    TenseID.ind_preterito.rawValue,
                    level: VerbLevel.basic.rawValue,
                    reviewContext: reviewContext,
                    referenceContext: referenceContext
                )
            case .allTenses:
                earned = hasUsedAllTenses(context: reviewContext)
            case .daily50:
                earned = progress.todayCount >= 50
            }

            if earned {
                progress.unlockBadge(badge.rawValue)
                newBadges.append(badge)
            }
        }

        return newBadges
    }

    // MARK: - Helpers

    private static func uniqueVerbsReviewed(context: ModelContext) -> Int {
        let descriptor = FetchDescriptor<ReviewCard>(
            predicate: #Predicate { $0.repetitions > 0 }
        )
        let cards = (try? context.fetch(descriptor)) ?? []
        let uniqueVerbs = Set(cards.map(\.verbId))
        return uniqueVerbs.count
    }

    private static func hasMasteredTense(
        _ tenseId: String,
        level: String,
        reviewContext: ModelContext,
        referenceContext: ModelContext
    ) -> Bool {
        let verbIds = Set(ReferenceStore(context: referenceContext).fetchVerbs(selectedLevel: level).map(\.id))
        guard !verbIds.isEmpty else { return false }

        let cardDescriptor = FetchDescriptor<ReviewCard>(
            predicate: #Predicate<ReviewCard> { card in
                card.tenseId == tenseId && card.interval >= 21
            }
        )
        let cards = (try? reviewContext.fetch(cardDescriptor)) ?? []
        let masteredVerbIds = Set(cards.map(\.verbId))

        return verbIds.isSubset(of: masteredVerbIds)
    }

    private static func hasUsedAllTenses(context: ModelContext) -> Bool {
        let descriptor = FetchDescriptor<ReviewCard>(
            predicate: #Predicate { $0.repetitions > 0 }
        )
        let cards = (try? context.fetch(descriptor)) ?? []
        let usedTenses = Set(cards.map(\.tenseId))
        let allTenseIds = Set(TenseInfo.all.map(\.id))
        return allTenseIds.isSubset(of: usedTenses)
    }
}
