import Foundation
import SharedModels
import SwiftData

enum PracticeMode: String, CaseIterable, Identifiable, Sendable {
    case flashcard
    case typing
    case multipleChoice
    case fullTable
    case handwriting
    case sentenceBuilder

    var id: String { rawValue }

    var label: String {
        switch self {
        case .flashcard: "Flashcard"
        case .typing: "Typing"
        case .multipleChoice: "Multiple Choice"
        case .fullTable: "Full Table"
        case .handwriting: "Handwriting"
        case .sentenceBuilder: "Sentence Builder"
        }
    }

    var icon: String {
        switch self {
        case .flashcard: "rectangle.on.rectangle"
        case .typing: "keyboard"
        case .multipleChoice: "list.bullet"
        case .fullTable: "tablecells"
        case .handwriting: "pencil.and.outline"
        case .sentenceBuilder: "text.word.spacing"
        }
    }
}

enum IrregularityFilter: Sendable {
    case spelling      // 1xx spans
    case stemChange    // 2xx spans
    case uniqueIrregular // 3xx spans
}

enum FocusMode: Sendable {
    case none
    case weakVerbs
    case irregularity(IrregularityFilter)
    case commonTenses
}

@MainActor
@Observable
final class PracticeViewModel {

    // MARK: - Current quiz state

    var currentVerb: Verb?
    var currentForm: VerbForm?
    var currentSpans: [IrregularSpan] = []
    var currentTenseInfo: TenseInfo?
    var currentPerson: String = ""

    var isAnswerRevealed: Bool = false
    var userAnswer: String = ""
    var isCorrect: Bool?

    // MARK: - Session stats

    var sessionCorrect: Int = 0
    var sessionTotal: Int = 0

    // MARK: - Mode

    var practiceMode: PracticeMode = .flashcard
    var multipleChoiceOptions: [String] = []
    var focusMode: FocusMode = .none

    // MARK: - Feedback

    var newBadges: [Badge] = []
    var isLoading: Bool = false
    var hasCards: Bool = true

    var sessionAccuracy: Double {
        guard sessionTotal > 0 else { return 0 }
        return Double(sessionCorrect) / Double(sessionTotal)
    }

    // MARK: - Load next card

    func loadNextCard(localContext: ModelContext, cloudContext: ModelContext) {
        isAnswerRevealed = false
        userAnswer = ""
        isCorrect = nil
        multipleChoiceOptions = []
        currentSpans = []
        hasCards = true
        isLoading = true
        let service = PracticeSessionService(
            localContext: localContext,
            cloudContext: cloudContext,
            reflexiveBaseInfinitives: ReflexiveVerbStore.shared.baseInfinitives
        )
        guard let cardLoad = service.nextCard(for: focusMode, excludingVerbId: currentVerb?.id) else {
            clearCurrentCard()
            hasCards = false
            isLoading = false
            return
        }

        applyCardLoad(cardLoad, localContext: localContext)
        isLoading = false
    }

    // MARK: - Answer checking

    func checkAnswer(_ answer: String) -> Bool {
        guard let form = currentForm else { return false }

        let trimmedAnswer = answer.trimmingCharacters(in: .whitespacesAndNewlines)
        let correctForm = form.form.trimmingCharacters(in: .whitespacesAndNewlines)

        // Case-insensitive but accent-sensitive comparison
        let result = trimmedAnswer.compare(correctForm, options: .caseInsensitive, locale: Locale(identifier: "es")) == .orderedSame

        isCorrect = result
        isAnswerRevealed = true

        if result {
            sessionCorrect += 1
        }
        sessionTotal += 1

        return result
    }

    func revealAnswer() {
        isAnswerRevealed = true
    }

    // MARK: - SRS rating

    func rateAnswer(quality: ReviewQuality, localContext: ModelContext, cloudContext: ModelContext) {
        guard let form = currentForm else { return }
        let service = PracticeSessionService(localContext: localContext, cloudContext: cloudContext)
        newBadges = service.rate(
            verbId: form.verbId,
            tenseId: form.tenseId,
            personIndex: form.personIndex,
            quality: quality
        )
    }

    // MARK: - Distractor generation

    func generateDistractors(for form: VerbForm, context: ModelContext) -> [String] {
        let personIndex = form.personIndex
        let correctForm = form.form
        let verbId = form.verbId
        let tenseId = form.tenseId

        // Same verb, same person, DIFFERENT tenses — much harder
        let descriptor = FetchDescriptor<VerbForm>(
            predicate: #Predicate<VerbForm> { f in
                f.verbId == verbId &&
                f.personIndex == personIndex &&
                f.tenseId != tenseId
            }
        )

        let samVerbForms = (try? context.fetch(descriptor)) ?? []

        var distractors: [String] = []
        var seen: Set<String> = [correctForm.lowercased()]

        for candidate in samVerbForms.shuffled() {
            let lower = candidate.form.lowercased()
            if !seen.contains(lower) {
                seen.insert(lower)
                distractors.append(candidate.form)
            }
            if distractors.count >= 3 { break }
        }

        // Fallback: if not enough same-verb forms, use same tense different verbs
        if distractors.count < 3 {
            let fallbackDescriptor = FetchDescriptor<VerbForm>(
                predicate: #Predicate<VerbForm> { f in
                    f.tenseId == tenseId &&
                    f.personIndex == personIndex &&
                    f.verbId != verbId
                }
            )
            let otherForms = (try? context.fetch(fallbackDescriptor)) ?? []
            for candidate in otherForms.shuffled() {
                let lower = candidate.form.lowercased()
                if !seen.contains(lower) {
                    seen.insert(lower)
                    distractors.append(candidate.form)
                }
                if distractors.count >= 3 { break }
            }
        }

        // Final fallback: fabricate
        while distractors.count < 3 {
            let filler = fabricateDistractor(from: correctForm, existing: seen)
            seen.insert(filler.lowercased())
            distractors.append(filler)
        }

        return distractors
    }

    // MARK: - Private helpers

    private func applyCardLoad(_ cardLoad: PracticeCardLoad, localContext: ModelContext) {
        currentVerb = cardLoad.verb
        currentForm = cardLoad.form
        currentSpans = cardLoad.spans
        currentTenseInfo = cardLoad.tenseInfo
        currentPerson = cardLoad.person

        if practiceMode == .multipleChoice {
            prepareMultipleChoice(for: cardLoad.form, context: localContext)
        }
    }

    private func clearCurrentCard() {
        currentVerb = nil
        currentForm = nil
        currentSpans = []
        currentTenseInfo = nil
        currentPerson = ""
    }

    private func prepareMultipleChoice(for form: VerbForm, context: ModelContext) {
        let distractors = generateDistractors(for: form, context: context)
        var options = distractors + [form.form]
        options.shuffle()
        multipleChoiceOptions = options
    }

    private func fabricateDistractor(from correct: String, existing: Set<String>) -> String {
        // Simple strategy: swap common Spanish verb endings
        let endings = ["o", "as", "a", "amos", "áis", "an",
                       "es", "e", "emos", "éis", "en",
                       "í", "iste", "ió", "imos", "isteis", "ieron"]

        for ending in endings.shuffled() {
            // Try replacing last 1-4 characters
            for len in [3, 2, 4, 1] {
                if correct.count > len {
                    let base = String(correct.prefix(correct.count - len))
                    let candidate = base + ending
                    if !existing.contains(candidate.lowercased()) && candidate != correct {
                        return candidate
                    }
                }
            }
        }

        // Fallback: append an extra letter
        return correct + "s"
    }
}
