import SwiftUI
import SharedModels
import SwiftData

/// English-first verb flashcards with two modes, switched from the toolbar:
///
/// - **Quiz** — the SRS path. `VocabSessionQueue` learning-step queue:
///   tap to reveal, rate Again/Hard/Good/Easy, cards requeue, graduation
///   feeds the long-term `VerbReviewStore` schedule.
/// - **Learn** — no-pressure browsing. Both sides shown at once (English +
///   Spanish + example), Next/Previous step through the same session pool
///   on a loop. No rating, no SRS side effects.
/// Which pool a flashcard session draws from.
enum VocabSessionKind {
    /// Due-first + new verbs, capped — the standard SRS session. Ratings
    /// update the long-term schedule.
    case standard
    /// Verbs already studied at least once, most-recent first, uncapped — a
    /// consolidation cram. Ratings drive the in-session queue only and do NOT
    /// reschedule (the long-term SM-2 due dates are left untouched).
    case reviewLearned
}

struct VocabFlashcardPracticeView: View {
    enum Mode: String { case quiz, learn }

    var kind: VocabSessionKind = .standard

    @Environment(\.modelContext) private var localContext
    @Environment(\.cloudModelContextProvider) private var cloudModelContextProvider
    @Environment(VerbExampleCache.self) private var exampleCache
    @Environment(\.dismiss) private var dismiss

    @AppStorage("vocabFlashcardMode") private var modeRaw: String = Mode.quiz.rawValue

    @State private var session: VocabSessionQueue?
    @State private var learnIndex: Int = 0
    @State private var revealed: Bool = false
    @State private var exampleByVerbId: [Int: VerbExample] = [:]
    @State private var generatingVerbIds: Set<Int> = []
    @State private var speech = SpeechService()

    private var cloudContext: ModelContext { cloudModelContextProvider() }

    /// The session's un-graduated verbs, derived live from the quiz queue so
    /// Learn mode walks exactly what's left to learn — it stays in sync as
    /// quiz mode graduates cards rather than browsing a stale frozen pool.
    private var sessionVerbs: [Verb] {
        session?.queue.map(\.verb) ?? []
    }

    private var mode: Mode {
        get { Mode(rawValue: modeRaw) ?? .quiz }
        nonmutating set { modeRaw = newValue.rawValue }
    }

    private var currentVerb: Verb? {
        switch mode {
        case .quiz:
            return session?.current?.verb
        case .learn:
            guard !sessionVerbs.isEmpty else { return nil }
            return sessionVerbs[learnIndex % sessionVerbs.count]
        }
    }

    var body: some View {
        ScrollView {
            VStack(spacing: 24) {
                headerBar
                switch mode {
                case .quiz:
                    quizContent
                case .learn:
                    learnContent
                }
            }
            .padding()
            .adaptiveContainer(maxWidth: 720)
        }
        .navigationTitle(kind == .reviewLearned ? "Review Learned" : "Vocab Flashcards")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .topBarTrailing) {
                Menu {
                    Picker("Mode", selection: Binding(
                        get: { mode },
                        set: { mode = $0 }
                    )) {
                        Label("Quiz (SRS)", systemImage: "checklist").tag(Mode.quiz)
                        Label("Learn", systemImage: "book").tag(Mode.learn)
                    }
                } label: {
                    Label(
                        mode == .learn ? "Learn" : "Quiz",
                        systemImage: mode == .learn ? "book" : "checklist"
                    )
                }
            }
        }
        .onAppear(perform: loadIfNeeded)
        .onChange(of: modeRaw) { _, _ in
            revealed = false
            primeExampleForCurrent()
        }
        .animation(.smooth, value: revealed)
        .animation(.smooth, value: currentVerb?.id)
    }

    // MARK: - Header

    @ViewBuilder
    private var headerBar: some View {
        VStack(spacing: 6) {
            switch mode {
            case .quiz:
                ProgressView(value: session?.progress ?? 0)
                    .tint(.purple)
                Text(quizProgressLabel)
                    .font(.caption)
                    .foregroundStyle(.secondary)
            case .learn:
                if sessionVerbs.isEmpty {
                    Text(emptyPoolMessage)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                } else {
                    Text("\(learnIndex % sessionVerbs.count + 1) of \(sessionVerbs.count)")
                        .font(.caption.weight(.medium))
                        .foregroundStyle(.secondary)
                }
            }

            if kind == .reviewLearned {
                Text("Practice pass — your review schedule won't change.")
                    .font(.caption2)
                    .foregroundStyle(.tertiary)
            }
        }
    }

    private var emptyPoolMessage: String {
        switch kind {
        case .standard:
            return "No verbs match the levels enabled in Settings"
        case .reviewLearned:
            return "Nothing studied yet — finish a Vocab Flashcards session first"
        }
    }

    private var quizProgressLabel: String {
        guard let session else { return "Loading…" }
        if session.isComplete { return "Done" }
        return "\(session.learnedCount) learned · \(session.remainingCount) to go"
    }

    // MARK: - Quiz mode

    @ViewBuilder
    private var quizContent: some View {
        if let verb = currentVerb {
            Text(verb.english)
                .font(.largeTitle.weight(.bold))
                .multilineTextAlignment(.center)
                .padding(.top, 12)

            if revealed {
                quizRevealed(verb)
            } else {
                tapToReveal
            }
        } else {
            completionView
        }
    }

    private var tapToReveal: some View {
        VStack(spacing: 8) {
            Image(systemName: "hand.tap")
                .font(.title)
                .foregroundStyle(.secondary)
            Text("Tap to reveal")
                .font(.headline)
                .foregroundStyle(.secondary)
        }
        .frame(maxWidth: .infinity)
        .frame(minHeight: 200)
        .glassEffect(in: RoundedRectangle(cornerRadius: 20))
        .contentShape(Rectangle())
        .onTapGesture {
            withAnimation(.smooth) { revealed = true }
        }
    }

    private func quizRevealed(_ verb: Verb) -> some View {
        VStack(spacing: 18) {
            spanishRow(verb)
            exampleBlock(for: verb)
            ratingButtons
        }
    }

    private var ratingButtons: some View {
        VStack(spacing: 10) {
            Text("How well did you know it?")
                .font(.subheadline)
                .foregroundStyle(.secondary)
            HStack(spacing: 10) {
                ratingButton("Again", color: .red, rating: .again)
                ratingButton("Hard", color: .orange, rating: .hard)
                ratingButton("Good", color: .green, rating: .good)
                ratingButton("Easy", color: .blue, rating: .easy)
            }
        }
    }

    private func ratingButton(_ label: String, color: Color, rating: VocabSessionQueue.Rating) -> some View {
        Button {
            answer(rating)
        } label: {
            Text(label)
                .font(.subheadline.weight(.semibold))
                .frame(maxWidth: .infinity)
                .padding(.vertical, 12)
        }
        .tint(color)
        .glassEffect(in: RoundedRectangle(cornerRadius: 12))
    }

    private var completionView: some View {
        VStack(spacing: 16) {
            Image(systemName: "checkmark.circle.fill")
                .font(.system(size: 56))
                .foregroundStyle(.green)
            Text(completionTitle)
                .font(.title2.bold())
            Text(completionDetail)
                .font(.subheadline)
                .foregroundStyle(.secondary)
                .multilineTextAlignment(.center)

            HStack(spacing: 12) {
                Button {
                    studyAgain()
                } label: {
                    Label(
                        kind == .reviewLearned ? "Study Again" : "Next Set",
                        systemImage: kind == .reviewLearned ? "arrow.clockwise" : "arrow.right"
                    )
                    .font(.subheadline.weight(.semibold))
                    .padding(.vertical, 6)
                    .frame(maxWidth: .infinity)
                }
                .buttonStyle(.borderedProminent)
                .tint(.purple)

                Button("Done") { dismiss() }
                    .font(.subheadline.weight(.semibold))
                    .padding(.vertical, 6)
                    .frame(maxWidth: .infinity)
                    .buttonStyle(.bordered)
            }
            .padding(.top, 12)
        }
        .padding(.top, 60)
        .padding(.horizontal, 24)
    }

    private var completionTitle: String {
        let learned = session?.learnedCount ?? 0
        switch kind {
        case .standard:
            return learned > 0 ? "Session Complete" : "Nothing Due"
        case .reviewLearned:
            return learned > 0 ? "Review Complete" : "Nothing to Review"
        }
    }

    private var completionDetail: String {
        let learned = session?.learnedCount ?? 0
        if learned > 0 {
            let noun = kind == .reviewLearned ? "reviewed" : "learned"
            return "\(learned) verb\(learned == 1 ? "" : "s") \(noun)"
        }
        switch kind {
        case .standard:
            return "No verbs are due right now. Study Again to review anyway."
        case .reviewLearned:
            return "Finish a Vocab Flashcards session first, then come back to consolidate."
        }
    }

    // MARK: - Learn mode

    @ViewBuilder
    private var learnContent: some View {
        if let verb = currentVerb {
            Text(verb.english)
                .font(.largeTitle.weight(.bold))
                .multilineTextAlignment(.center)
                .padding(.top, 12)

            spanishRow(verb)
            exampleBlock(for: verb)

            HStack(spacing: 12) {
                Button {
                    learnStep(-1)
                } label: {
                    Label("Previous", systemImage: "chevron.left")
                        .font(.subheadline.weight(.semibold))
                        .frame(maxWidth: .infinity)
                        .padding(.vertical, 12)
                }
                .tint(.secondary)
                .glassEffect(in: RoundedRectangle(cornerRadius: 12))

                Button {
                    learnStep(1)
                } label: {
                    Label("Next", systemImage: "chevron.right")
                        .labelStyle(.titleAndIcon)
                        .font(.subheadline.weight(.semibold))
                        .frame(maxWidth: .infinity)
                        .padding(.vertical, 12)
                }
                .tint(.purple)
                .glassEffect(in: RoundedRectangle(cornerRadius: 12))
            }
        } else {
            ContentUnavailableView(
                "Nothing to Learn",
                systemImage: "book",
                description: Text("Enable some verb levels in Settings.")
            )
            .padding(.top, 40)
        }
    }

    private func learnStep(_ delta: Int) {
        guard !sessionVerbs.isEmpty else { return }
        let count = sessionVerbs.count
        learnIndex = ((learnIndex + delta) % count + count) % count
        primeExampleForCurrent()
    }

    // MARK: - Shared card pieces

    private func spanishRow(_ verb: Verb) -> some View {
        HStack(spacing: 12) {
            Text(verb.infinitive)
                .font(.title.weight(.semibold))
                .multilineTextAlignment(.center)

            Button {
                speech.speak(verb.infinitive)
            } label: {
                Image(systemName: "speaker.wave.2.fill")
                    .font(.title3)
                    .padding(10)
            }
            .glassEffect(in: .circle)
            .accessibilityLabel("Say it out loud")
        }
    }

    @ViewBuilder
    private func exampleBlock(for verb: Verb) -> some View {
        if let example = exampleByVerbId[verb.id] {
            VStack(alignment: .leading, spacing: 4) {
                Text(example.spanish).font(.subheadline).italic()
                Text(example.english).font(.caption).foregroundStyle(.secondary)
            }
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding()
            .background(.fill.quinary, in: RoundedRectangle(cornerRadius: 12))
        } else if generatingVerbIds.contains(verb.id) {
            HStack(spacing: 8) {
                ProgressView()
                Text("Generating example…")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
            .frame(maxWidth: .infinity)
            .padding()
            .background(.fill.quinary, in: RoundedRectangle(cornerRadius: 12))
        }
    }

    // MARK: - Logic

    private func loadIfNeeded() {
        guard session == nil else { return }
        switch kind {
        case .standard:
            loadStandardSession()
        case .reviewLearned:
            let verbs = VocabVerbPool.reviewLearnedVerbs(localContext: localContext, cloudContext: cloudContext)
            session = VocabSessionQueue(verbs: verbs)
        }
        primeExampleForCurrent()
    }

    /// Resume the persisted, cross-device study group if one is active;
    /// otherwise start a fresh group and persist it.
    private func loadStandardSession() {
        let store = VocabStudyGroupStore(context: cloudContext)
        if let group = store.activeGroup() {
            let stored = group.entries
            if !stored.isEmpty {
                let byId = verbsByID(Set(stored.map(\.verbId)))
                let entries: [(verb: Verb, state: VocabSessionQueue.CardState)] = stored.compactMap { e in
                    guard let verb = byId[e.verbId] else { return nil }
                    return (verb, VocabSessionQueue.CardState(rawValue: e.state) ?? .new)
                }
                // Resume only if EVERY stored verb resolved. A partial resume
                // would desync learnedCount from a shrunken queue and then
                // persist that loss — fall through to a fresh rebuild instead.
                if entries.count == stored.count {
                    session = VocabSessionQueue(entries: entries, learnedCount: group.learnedCount)
                    return
                }
            }
        }
        // No active group (or it couldn't be fully resolved) — start fresh and
        // persist immediately so closing the app right away still resumes this set.
        let verbs = VocabVerbPool.sessionVerbs(localContext: localContext, cloudContext: cloudContext)
        session = VocabSessionQueue(verbs: verbs)
        persistGroup()
    }

    private func verbsByID(_ ids: Set<Int>) -> [Int: Verb] {
        let all = (try? localContext.fetch(FetchDescriptor<Verb>())) ?? []
        var map: [Int: Verb] = [:]
        for verb in all where ids.contains(verb.id) { map[verb.id] = verb }
        return map
    }

    /// Write the standard session's progress to the cloud-synced study group,
    /// or clear the group when the set is fully learned.
    private func persistGroup() {
        guard kind == .standard, let session else { return }
        let store = VocabStudyGroupStore(context: cloudContext)
        if session.isComplete {
            store.clear()
        } else {
            let entries = session.snapshot().map {
                StoredVocabEntry(verbId: $0.verbId, state: $0.state.rawValue)
            }
            store.persist(entries: entries, learnedCount: session.learnedCount)
        }
    }

    private func studyAgain() {
        switch kind {
        case .standard:
            // Clear the finished group explicitly before building a fresh one,
            // so a fresh set is never appended onto a stale group record.
            VocabStudyGroupStore(context: cloudContext).clear()
            let verbs = VocabVerbPool.sessionVerbs(localContext: localContext, cloudContext: cloudContext)
            session = VocabSessionQueue(verbs: verbs)
            persistGroup()
        case .reviewLearned:
            session?.restart()
        }
        learnIndex = 0
        revealed = false
        primeExampleForCurrent()
    }

    private func answer(_ rating: VocabSessionQueue.Rating) {
        guard let verbId = currentVerb?.id else { return }
        let graduation = session?.answer(rating) ?? nil
        // Review Learned is a cram pass — graduation drives the in-session
        // queue only; the long-term SM-2 schedule is left untouched.
        if let graduation, kind == .standard {
            VerbReviewStore(context: cloudContext).rate(verbId: verbId, quality: graduation)
        }
        persistGroup()
        withAnimation(.smooth) { revealed = false }
        primeExampleForCurrent()
    }

    private func primeExampleForCurrent() {
        guard let verb = currentVerb else { return }
        let verbId = verb.id
        if exampleByVerbId[verbId] != nil || generatingVerbIds.contains(verbId) { return }

        if let cached = exampleCache.examples(for: verbId)?.first {
            exampleByVerbId[verbId] = cached
            return
        }
        guard VerbExampleGenerator.isAvailable else { return }
        generatingVerbIds.insert(verbId)
        let infinitive = verb.infinitive
        let english = verb.english
        let formsByTense = ReferenceStore(context: localContext)
            .conjugatedForms(verbId: verbId, tenseIds: VocabExampleTenseIds.canonical)
        Task {
            do {
                let examples = try await VerbExampleGenerator.generate(
                    verbInfinitive: infinitive,
                    verbEnglish: english,
                    tenseIds: VocabExampleTenseIds.canonical,
                    formsByTense: formsByTense
                )
                exampleCache.setExamples(examples, for: verbId)
                let pick = examples.first { $0.tenseId == "ind_presente" } ?? examples.first
                if let pick, currentVerb?.id == verbId {
                    exampleByVerbId[verbId] = pick
                }
            } catch {
                // Silent — the example block just stays hidden.
            }
            generatingVerbIds.remove(verbId)
        }
    }
}
