import Foundation
import SharedModels
import SwiftData

struct PracticeSettings: Sendable {
    let selectedLevel: String
    let enabledTenses: Set<String>
    let showVosotros: Bool

    init(progress: UserProgress?) {
        let resolved = progress?.enabledTenseIDs ?? []
        self.selectedLevel = progress?.selectedLevel ?? VerbLevel.basic.rawValue
        self.enabledTenses = Set(resolved)
        self.showVosotros = progress?.showVosotros ?? true
    }

    var selectionTenseIDs: [String] {
        enabledTenses.isEmpty ? TenseID.defaultPracticeIDs : Array(enabledTenses)
    }
}

struct PracticeCardLoad {
    let verb: Verb
    let form: VerbForm
    let spans: [IrregularSpan]
    let tenseInfo: TenseInfo?
    let person: String
}

struct FullTablePrompt {
    let verb: Verb
    let tenseInfo: TenseInfo
    let forms: [VerbForm]
}

struct PracticeSessionService {
    let localContext: ModelContext
    let cloudContext: ModelContext
    private let referenceStore: ReferenceStore

    init(localContext: ModelContext, cloudContext: ModelContext) {
        self.localContext = localContext
        self.cloudContext = cloudContext
        self.referenceStore = ReferenceStore(context: localContext)
    }

    func settings() -> PracticeSettings {
        PracticeSettings(progress: ReviewStore.fetchOrCreateUserProgress(context: cloudContext))
    }

    func nextCard(for focusMode: FocusMode, excludingVerbId lastVerbId: Int? = nil) -> PracticeCardLoad? {
        switch focusMode {
        case .weakVerbs:
            if let form = pickWeakForm() {
                return loadCard(from: form)
            }
        case .irregularity(let filter):
            if let form = pickIrregularForm(filter: filter) {
                return loadCard(from: form)
            }
        case .commonTenses:
            if let form = pickCommonTenseForm() {
                return loadCard(from: form)
            }
        case .none:
            break
        }

        if let dueCard = fetchDueCard(excluding: lastVerbId) {
            return loadCard(from: dueCard)
        }

        if let form = pickRandomForm() {
            return loadCard(from: form)
        }

        return nil
    }

    func randomFullTablePrompt() -> FullTablePrompt? {
        let settings = settings()
        let verbs = referenceStore.fetchVerbs(selectedLevel: settings.selectedLevel)
        guard !verbs.isEmpty else { return nil }

        for _ in 0..<40 {
            guard let verb = verbs.randomElement(),
                  let tenseId = settings.selectionTenseIDs.randomElement(),
                  let tenseInfo = TenseInfo.find(tenseId) else { continue }

            let forms = referenceStore.fetchForms(verbId: verb.id, tenseId: tenseId)
            if forms.isEmpty { continue }

            // Full Table practice is for regular patterns only — skip combos
            // where any form in this (verb, tense) is irregular.
            if forms.contains(where: { $0.regularity != "regular" }) { continue }

            return FullTablePrompt(verb: verb, tenseInfo: tenseInfo, forms: forms)
        }

        return nil
    }

    func rate(verbId: Int, tenseId: String, personIndex: Int, quality: ReviewQuality) -> [Badge] {
        ReviewStore.recordReview(
            verbId: verbId,
            tenseId: tenseId,
            personIndex: personIndex,
            quality: quality,
            context: cloudContext,
            referenceContext: localContext
        )
    }

    func recordFullTableReview(verbId: Int, tenseId: String, results: [Int: Bool]) -> [Badge] {
        ReviewStore.recordFullTableReview(
            verbId: verbId,
            tenseId: tenseId,
            results: results,
            context: cloudContext,
            referenceContext: localContext
        )
    }

    func loadCard(from reviewCard: ReviewCard) -> PracticeCardLoad? {
        guard let verb = referenceStore.fetchVerb(id: reviewCard.verbId),
              let form = referenceStore.fetchForm(
                verbId: reviewCard.verbId,
                tenseId: reviewCard.tenseId,
                personIndex: reviewCard.personIndex
              ) else { return nil }
        return buildCardLoad(verb: verb, form: form)
    }

    func loadCard(from form: VerbForm) -> PracticeCardLoad? {
        guard let verb = referenceStore.fetchVerb(id: form.verbId) else { return nil }
        return buildCardLoad(verb: verb, form: form)
    }

    private func buildCardLoad(verb: Verb, form: VerbForm) -> PracticeCardLoad {
        let spans = referenceStore.fetchSpans(
            verbId: form.verbId,
            tenseId: form.tenseId,
            personIndex: form.personIndex
        )
        let person = TenseInfo.persons.indices.contains(form.personIndex)
            ? TenseInfo.persons[form.personIndex]
            : ""

        return PracticeCardLoad(
            verb: verb,
            form: form,
            spans: spans,
            tenseInfo: TenseInfo.find(form.tenseId),
            person: person
        )
    }

    private func fetchDueCard(excluding lastVerbId: Int?) -> ReviewCard? {
        let settings = settings()
        let allowedVerbIds = referenceStore.allowedVerbIDs(selectedLevel: settings.selectedLevel)
        let now = Date()
        var descriptor = FetchDescriptor<ReviewCard>(
            predicate: #Predicate<ReviewCard> { $0.dueDate <= now },
            sortBy: [SortDescriptor(\ReviewCard.dueDate)]
        )
        descriptor.fetchLimit = settings.enabledTenses.isEmpty ? 10 : 50
        let cards = (try? cloudContext.fetch(descriptor)) ?? []

        let eligible = cards.filter { card in
            allowedVerbIds.contains(card.verbId) &&
            (settings.enabledTenses.isEmpty || settings.enabledTenses.contains(card.tenseId)) &&
            (settings.showVosotros || card.personIndex != 4)
        }

        // Prefer a card from a different verb than the last one shown.
        // Fall back to the same verb only if it's the sole due card.
        if let lastVerbId {
            if let different = eligible.first(where: { $0.verbId != lastVerbId }) {
                return different
            }
        }
        return eligible.first
    }

    private func pickWeakForm() -> VerbForm? {
        let settings = settings()
        let allowedVerbIds = referenceStore.allowedVerbIDs(selectedLevel: settings.selectedLevel)

        let descriptor = FetchDescriptor<ReviewCard>(
            predicate: #Predicate<ReviewCard> { $0.easeFactor < 2.0 && $0.repetitions > 0 },
            sortBy: [SortDescriptor(\ReviewCard.easeFactor)]
        )
        let cards = ((try? cloudContext.fetch(descriptor)) ?? []).filter { card in
            allowedVerbIds.contains(card.verbId) &&
            (settings.enabledTenses.isEmpty || settings.enabledTenses.contains(card.tenseId)) &&
            (settings.showVosotros || card.personIndex != 4)
        }

        guard let card = cards.randomElement() else { return nil }
        return referenceStore.fetchForm(
            verbId: card.verbId,
            tenseId: card.tenseId,
            personIndex: card.personIndex
        )
    }

    private func pickIrregularForm(filter: IrregularityFilter) -> VerbForm? {
        let settings = settings()
        let allowedVerbIds = referenceStore.allowedVerbIDs(selectedLevel: settings.selectedLevel)
        let typeRange: ClosedRange<Int>

        switch filter {
        case .spelling:
            typeRange = 100...199
        case .stemChange:
            typeRange = 200...299
        case .uniqueIrregular:
            typeRange = 300...399
        }

        var descriptor = FetchDescriptor<IrregularSpan>(
            predicate: #Predicate<IrregularSpan> { span in
                span.spanType >= typeRange.lowerBound &&
                span.spanType <= typeRange.upperBound
            }
        )
        descriptor.fetchLimit = 500

        let spans = ((try? localContext.fetch(descriptor)) ?? []).filter { span in
            allowedVerbIds.contains(span.verbId) &&
            (settings.enabledTenses.isEmpty || settings.enabledTenses.contains(span.tenseId)) &&
            (settings.showVosotros || span.personIndex != 4)
        }

        guard let span = spans.randomElement() else { return nil }
        return referenceStore.fetchForm(
            verbId: span.verbId,
            tenseId: span.tenseId,
            personIndex: span.personIndex
        )
    }

    private func pickCommonTenseForm() -> VerbForm? {
        let settings = settings()
        let coreTenseIDs = TenseID.coreTenseIDs
        let verbs = referenceStore.fetchVerbs(selectedLevel: settings.selectedLevel)
        guard let verb = verbs.randomElement() else { return nil }

        let forms = referenceStore.fetchVerbForms(verbId: verb.id).filter { form in
            coreTenseIDs.contains(form.tenseId) &&
            (settings.showVosotros || form.personIndex != 4)
        }

        return forms.randomElement()
    }

    private func pickRandomForm() -> VerbForm? {
        let settings = settings()
        let verbs = referenceStore.fetchVerbs(selectedLevel: settings.selectedLevel)
        guard let verb = verbs.randomElement() else { return nil }

        let forms = referenceStore.fetchVerbForms(verbId: verb.id).filter { form in
            (settings.enabledTenses.isEmpty || settings.enabledTenses.contains(form.tenseId)) &&
            (settings.showVosotros || form.personIndex != 4)
        }

        return forms.randomElement()
    }
}
