import Foundation
import SharedModels
import SwiftData

@MainActor
@Observable
final class DictionaryService {

    struct Entry {
        let word: String
        let baseForm: String
        let english: String
        let partOfSpeech: String
        let tenseId: String?
        let person: String?
    }

    private var verbIndex: [String: Entry] = [:]
    private var nonVerbIndex: [String: Entry] = [:]
    private var isBuilt = false

    /// Build the reverse index from existing verb data + bundled non-verb dictionary.
    /// Loads from disk cache if available, otherwise builds from DB and caches.
    func buildIfNeeded(context: ModelContext) {
        guard !isBuilt else { return }

        loadNonVerbDictionary()

        if loadCachedIndex() {
            isBuilt = true
            return
        }

        // No cache — build from DB
        let verbDescriptor = FetchDescriptor<Verb>()
        let verbs = (try? context.fetch(verbDescriptor)) ?? []
        let verbMap = Dictionary(uniqueKeysWithValues: verbs.map { ($0.id, $0) })

        let formDescriptor = FetchDescriptor<VerbForm>()
        let forms = (try? context.fetch(formDescriptor)) ?? []

        let persons = TenseInfo.persons
        for form in forms {
            guard let verb = verbMap[form.verbId] else { continue }
            let key = form.form.lowercased()
            if verbIndex[key] != nil { continue }

            let person = form.personIndex < persons.count ? persons[form.personIndex] : nil
            verbIndex[key] = Entry(
                word: form.form,
                baseForm: verb.infinitive,
                english: verb.english,
                partOfSpeech: "verb",
                tenseId: form.tenseId,
                person: person
            )
        }

        for verb in verbs {
            let key = verb.infinitive.lowercased()
            if verbIndex[key] == nil {
                verbIndex[key] = Entry(
                    word: verb.infinitive,
                    baseForm: verb.infinitive,
                    english: verb.english,
                    partOfSpeech: "verb",
                    tenseId: nil,
                    person: nil
                )
            }
        }

        isBuilt = true
        saveCachedIndex()
        print("[Dictionary] Built index from DB: \(verbIndex.count) verb forms")
    }

    // MARK: - Disk Cache

    private static var cacheURL: URL {
        FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
            .appendingPathComponent("dictionary_index.json")
    }

    private struct CachedEntry: Codable {
        let word: String
        let baseForm: String
        let english: String
        let partOfSpeech: String
        let tenseId: String?
        let person: String?
    }

    private func saveCachedIndex() {
        let entries = verbIndex.map { (key: $0.key, value: CachedEntry(
            word: $0.value.word, baseForm: $0.value.baseForm,
            english: $0.value.english, partOfSpeech: $0.value.partOfSpeech,
            tenseId: $0.value.tenseId, person: $0.value.person
        ))}
        let dict = Dictionary(uniqueKeysWithValues: entries)
        if let data = try? JSONEncoder().encode(dict) {
            try? data.write(to: Self.cacheURL)
        }
    }

    private func loadCachedIndex() -> Bool {
        guard let data = try? Data(contentsOf: Self.cacheURL),
              let dict = try? JSONDecoder().decode([String: CachedEntry].self, from: data) else {
            return false
        }
        verbIndex = dict.mapValues { Entry(
            word: $0.word, baseForm: $0.baseForm,
            english: $0.english, partOfSpeech: $0.partOfSpeech,
            tenseId: $0.tenseId, person: $0.person
        )}
        print("[Dictionary] Loaded cached index: \(verbIndex.count) verb forms")
        return true
    }

    func lookup(_ word: String) -> Entry? {
        let cleaned = word.lowercased()
            .trimmingCharacters(in: .punctuationCharacters)
            .trimmingCharacters(in: .whitespaces)

        return verbIndex[cleaned] ?? nonVerbIndex[cleaned]
    }

    private func loadNonVerbDictionary() {
        // Common non-verb Spanish words — articles, prepositions, pronouns, adjectives, nouns, adverbs, conjunctions
        let words: [(String, String, String)] = [
            // Articles
            ("el", "the (masc.)", "article"), ("la", "the (fem.)", "article"),
            ("los", "the (masc. pl.)", "article"), ("las", "the (fem. pl.)", "article"),
            ("un", "a, an (masc.)", "article"), ("una", "a, an (fem.)", "article"),
            ("unos", "some (masc.)", "article"), ("unas", "some (fem.)", "article"),

            // Pronouns
            ("yo", "I", "pronoun"), ("tú", "you (informal)", "pronoun"),
            ("él", "he", "pronoun"), ("ella", "she", "pronoun"),
            ("nosotros", "we (masc.)", "pronoun"), ("nosotras", "we (fem.)", "pronoun"),
            ("ellos", "they (masc.)", "pronoun"), ("ellas", "they (fem.)", "pronoun"),
            ("usted", "you (formal)", "pronoun"), ("ustedes", "you all (formal)", "pronoun"),
            ("me", "me", "pronoun"), ("te", "you (obj.)", "pronoun"),
            ("nos", "us", "pronoun"), ("le", "him/her/you (obj.)", "pronoun"),
            ("les", "them/you all (obj.)", "pronoun"), ("lo", "it/him (obj.)", "pronoun"),
            ("se", "self/each other", "pronoun"), ("mi", "my", "pronoun"),
            ("tu", "your (informal)", "pronoun"), ("su", "his/her/your/their", "pronoun"),
            ("nuestro", "our (masc.)", "pronoun"), ("nuestra", "our (fem.)", "pronoun"),
            ("esto", "this", "pronoun"), ("eso", "that", "pronoun"),
            ("algo", "something", "pronoun"), ("nada", "nothing", "pronoun"),
            ("alguien", "someone", "pronoun"), ("nadie", "nobody", "pronoun"),
            ("todo", "everything, all", "pronoun"), ("cada", "each", "pronoun"),
            ("otro", "other, another", "pronoun"), ("otra", "other, another (fem.)", "pronoun"),
            ("mismo", "same, self", "pronoun"), ("misma", "same, self (fem.)", "pronoun"),

            // Prepositions
            ("a", "to, at", "preposition"), ("de", "of, from", "preposition"),
            ("en", "in, on, at", "preposition"), ("con", "with", "preposition"),
            ("por", "for, by, through", "preposition"), ("para", "for, in order to", "preposition"),
            ("sin", "without", "preposition"), ("sobre", "on, about", "preposition"),
            ("entre", "between, among", "preposition"), ("hasta", "until, up to", "preposition"),
            ("desde", "from, since", "preposition"), ("hacia", "toward", "preposition"),
            ("durante", "during", "preposition"), ("según", "according to", "preposition"),
            ("tras", "after, behind", "preposition"), ("contra", "against", "preposition"),

            // Conjunctions
            ("y", "and", "conjunction"), ("e", "and (before i/hi)", "conjunction"),
            ("o", "or", "conjunction"), ("u", "or (before o/ho)", "conjunction"),
            ("pero", "but", "conjunction"), ("sino", "but rather", "conjunction"),
            ("porque", "because", "conjunction"), ("que", "that, which", "conjunction"),
            ("si", "if", "conjunction"), ("cuando", "when", "conjunction"),
            ("como", "as, like, how", "conjunction"), ("donde", "where", "conjunction"),
            ("aunque", "although", "conjunction"), ("mientras", "while", "conjunction"),
            ("ni", "neither, nor", "conjunction"), ("pues", "well, since", "conjunction"),

            // Common adverbs
            ("no", "no, not", "adverb"), ("sí", "yes", "adverb"),
            ("muy", "very", "adverb"), ("más", "more, most", "adverb"),
            ("menos", "less, fewer", "adverb"), ("bien", "well", "adverb"),
            ("mal", "badly", "adverb"), ("ya", "already, now", "adverb"),
            ("también", "also, too", "adverb"), ("tampoco", "neither, either", "adverb"),
            ("aquí", "here", "adverb"), ("ahí", "there", "adverb"),
            ("allí", "over there", "adverb"), ("siempre", "always", "adverb"),
            ("nunca", "never", "adverb"), ("hoy", "today", "adverb"),
            ("ayer", "yesterday", "adverb"), ("mañana", "tomorrow", "adverb"),
            ("ahora", "now", "adverb"), ("después", "after, later", "adverb"),
            ("antes", "before", "adverb"), ("luego", "then, later", "adverb"),
            ("todavía", "still, yet", "adverb"), ("casi", "almost", "adverb"),
            ("solo", "only, alone", "adverb"), ("tan", "so, as", "adverb"),
            ("mucho", "a lot, much", "adverb"), ("poco", "little, few", "adverb"),
            ("bastante", "quite, enough", "adverb"), ("demasiado", "too much", "adverb"),

            // Question words
            ("qué", "what", "interrogative"), ("quién", "who", "interrogative"),
            ("cómo", "how", "interrogative"), ("dónde", "where", "interrogative"),
            ("cuándo", "when", "interrogative"), ("cuánto", "how much", "interrogative"),
            ("cuál", "which", "interrogative"), ("por qué", "why", "interrogative"),

            // Common nouns
            ("casa", "house", "noun"), ("hombre", "man", "noun"),
            ("mujer", "woman", "noun"), ("niño", "boy, child", "noun"),
            ("niña", "girl", "noun"), ("familia", "family", "noun"),
            ("amigo", "friend (masc.)", "noun"), ("amiga", "friend (fem.)", "noun"),
            ("tiempo", "time, weather", "noun"), ("día", "day", "noun"),
            ("noche", "night", "noun"), ("año", "year", "noun"),
            ("vida", "life", "noun"), ("mundo", "world", "noun"),
            ("país", "country", "noun"), ("ciudad", "city", "noun"),
            ("agua", "water", "noun"), ("comida", "food", "noun"),
            ("trabajo", "work, job", "noun"), ("escuela", "school", "noun"),
            ("libro", "book", "noun"), ("calle", "street", "noun"),
            ("dinero", "money", "noun"), ("mano", "hand", "noun"),
            ("padre", "father", "noun"), ("madre", "mother", "noun"),
            ("hijo", "son", "noun"), ("hija", "daughter", "noun"),
            ("hermano", "brother", "noun"), ("hermana", "sister", "noun"),
            ("persona", "person", "noun"), ("gente", "people", "noun"),
            ("cosa", "thing", "noun"), ("lugar", "place", "noun"),
            ("parte", "part", "noun"), ("nombre", "name", "noun"),
            ("momento", "moment", "noun"), ("problema", "problem", "noun"),
            ("mesa", "table", "noun"), ("puerta", "door", "noun"),
            ("coche", "car", "noun"), ("perro", "dog", "noun"),
            ("gato", "cat", "noun"), ("sol", "sun", "noun"),
            ("mar", "sea", "noun"), ("playa", "beach", "noun"),
            ("montaña", "mountain", "noun"), ("tienda", "store", "noun"),
            ("restaurante", "restaurant", "noun"), ("hotel", "hotel", "noun"),
            ("cuerpo", "body", "noun"), ("cabeza", "head", "noun"),
            ("corazón", "heart", "noun"), ("ojo", "eye", "noun"),

            // Common adjectives
            ("bueno", "good", "adjective"), ("buena", "good (fem.)", "adjective"),
            ("malo", "bad", "adjective"), ("mala", "bad (fem.)", "adjective"),
            ("grande", "big, great", "adjective"), ("pequeño", "small", "adjective"),
            ("nuevo", "new", "adjective"), ("viejo", "old", "adjective"),
            ("joven", "young", "adjective"), ("largo", "long", "adjective"),
            ("corto", "short", "adjective"), ("alto", "tall, high", "adjective"),
            ("bajo", "short, low", "adjective"), ("bonito", "pretty", "adjective"),
            ("hermoso", "beautiful", "adjective"), ("feo", "ugly", "adjective"),
            ("feliz", "happy", "adjective"), ("triste", "sad", "adjective"),
            ("fácil", "easy", "adjective"), ("difícil", "difficult", "adjective"),
            ("importante", "important", "adjective"), ("posible", "possible", "adjective"),
            ("mejor", "better, best", "adjective"), ("peor", "worse, worst", "adjective"),
            ("primero", "first", "adjective"), ("último", "last", "adjective"),
            ("mismo", "same", "adjective"), ("otro", "other", "adjective"),
            ("cada", "each, every", "adjective"), ("todo", "all, every", "adjective"),
            ("mucho", "much, many", "adjective"), ("poco", "little, few", "adjective"),

            // Numbers
            ("uno", "one", "number"), ("dos", "two", "number"),
            ("tres", "three", "number"), ("cuatro", "four", "number"),
            ("cinco", "five", "number"), ("seis", "six", "number"),
            ("siete", "seven", "number"), ("ocho", "eight", "number"),
            ("nueve", "nine", "number"), ("diez", "ten", "number"),

            // Misc
            ("del", "of the (de + el)", "contraction"), ("al", "to the (a + el)", "contraction"),
        ]

        for (word, english, pos) in words {
            nonVerbIndex[word.lowercased()] = Entry(
                word: word,
                baseForm: word,
                english: english,
                partOfSpeech: pos,
                tenseId: nil,
                person: nil
            )
        }
    }
}
