import Foundation
import SharedModels
import SwiftData

struct ReferenceStore {
    let context: ModelContext

    func fetchGuides() -> [TenseGuide] {
        let descriptor = FetchDescriptor<TenseGuide>(sortBy: [SortDescriptor(\TenseGuide.tenseId)])
        return (try? context.fetch(descriptor)) ?? []
    }

    func fetchGuideMap() -> [String: TenseGuide] {
        Dictionary(uniqueKeysWithValues: fetchGuides().map { ($0.tenseId, $0) })
    }

    func fetchVerbs() -> [Verb] {
        let descriptor = FetchDescriptor<Verb>(sortBy: [SortDescriptor(\Verb.infinitive)])
        return (try? context.fetch(descriptor)) ?? []
    }

    func fetchVerbs(selectedLevel: String) -> [Verb] {
        fetchVerbs().filter { VerbLevelGroup.matches($0.level, selectedLevel: selectedLevel) }
    }

    func allowedVerbIDs(selectedLevel: String) -> Set<Int> {
        Set(fetchVerbs(selectedLevel: selectedLevel).map(\.id))
    }

    /// Union of data-levels for all selected user-facing levels.
    /// Empty input produces an empty result — callers decide how to handle that.
    func fetchVerbs(selectedLevels: Set<String>) -> [Verb] {
        guard !selectedLevels.isEmpty else { return [] }
        let ids = PracticeFilter.verbIDs(
            matchingLevels: selectedLevels,
            in: fetchVerbs().map { .init(id: $0.id, level: $0.level) }
        )
        return fetchVerbs().filter { ids.contains($0.id) }
    }

    /// Practice verb pool intersecting selected levels with selected irregular-span categories.
    /// Delegates to `PracticeFilter` so the intersection logic is unit-tested
    /// in SharedModels without a ModelContainer (Issue #26).
    func allowedVerbIDs(
        selectedLevels: Set<String>,
        irregularCategories: Set<IrregularSpan.SpanCategory>
    ) -> Set<Int> {
        PracticeFilter.allowedVerbIDs(
            verbs: fetchVerbs().map { .init(id: $0.id, level: $0.level) },
            spans: allIrregularSlots(),
            selectedLevels: selectedLevels,
            irregularCategories: irregularCategories
        )
    }

    /// Convenience: full Verb objects passing both filters.
    func fetchVerbs(
        selectedLevels: Set<String>,
        irregularCategories: Set<IrregularSpan.SpanCategory>
    ) -> [Verb] {
        let ids = allowedVerbIDs(
            selectedLevels: selectedLevels,
            irregularCategories: irregularCategories
        )
        return fetchVerbs().filter { ids.contains($0.id) }
    }

    private func allIrregularSlots() -> [PracticeFilter.IrregularSlot] {
        let descriptor = FetchDescriptor<IrregularSpan>()
        let spans = (try? context.fetch(descriptor)) ?? []
        return spans.map { .init(verbId: $0.verbId, category: $0.category) }
    }

    func fetchVerb(id: Int) -> Verb? {
        let descriptor = FetchDescriptor<Verb>(predicate: #Predicate<Verb> { $0.id == id })
        return (try? context.fetch(descriptor))?.first
    }

    func fetchVerbForms(verbId: Int) -> [VerbForm] {
        let descriptor = FetchDescriptor<VerbForm>(
            predicate: #Predicate<VerbForm> { $0.verbId == verbId },
            sortBy: [SortDescriptor(\VerbForm.tenseId), SortDescriptor(\VerbForm.personIndex)]
        )
        return (try? context.fetch(descriptor)) ?? []
    }

    func fetchForms(verbId: Int, tenseId: String) -> [VerbForm] {
        let descriptor = FetchDescriptor<VerbForm>(
            predicate: #Predicate<VerbForm> { form in
                form.verbId == verbId && form.tenseId == tenseId
            },
            sortBy: [SortDescriptor(\VerbForm.personIndex)]
        )
        return (try? context.fetch(descriptor)) ?? []
    }

    /// Map of tenseId → conjugated forms for a verb, used to ground and
    /// validate LLM-generated example sentences.
    func conjugatedForms(verbId: Int, tenseIds: [String]) -> [String: [String]] {
        var map: [String: [String]] = [:]
        for tenseId in tenseIds {
            let forms = fetchForms(verbId: verbId, tenseId: tenseId)
                .map(\.form)
                .filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
            if !forms.isEmpty { map[tenseId] = forms }
        }
        return map
    }

    func fetchForm(verbId: Int, tenseId: String, personIndex: Int) -> VerbForm? {
        let descriptor = FetchDescriptor<VerbForm>(
            predicate: #Predicate<VerbForm> { form in
                form.verbId == verbId &&
                form.tenseId == tenseId &&
                form.personIndex == personIndex
            }
        )
        return (try? context.fetch(descriptor))?.first
    }

    func fetchSpans(verbId: Int, tenseId: String, personIndex: Int) -> [IrregularSpan] {
        let descriptor = FetchDescriptor<IrregularSpan>(
            predicate: #Predicate<IrregularSpan> { span in
                span.verbId == verbId &&
                span.tenseId == tenseId &&
                span.personIndex == personIndex
            },
            sortBy: [SortDescriptor(\IrregularSpan.start)]
        )
        return (try? context.fetch(descriptor)) ?? []
    }
}
