from __future__ import annotations

import json
from datetime import datetime
from pathlib import Path
from typing import Dict, List

from build_vocabulary import (
    FILTERED_OUTPUT_PATH,
    METADATA_OUTPUT_PATH,
    build_vocabulary,
)


LEXICON_OUTPUT_PATH = Path(__file__).with_name("lexicon_it.json")

POS_BY_TAG = {
    "function": "PREP",
    "verb_infinitive": "VERB",
    "adverb": "ADV",
    "adjective_like": "ADJ",
    "noun_like": "NOUN",
}

REGISTER_BY_QUALITY = [
    (8, "common"),
    (5, "standard"),
    (3, "formal"),
    (0, "rare"),
]

TOPIC_KEYWORDS = {
    "animals": {
        "cane", "gatto", "lupo", "volpe", "orso", "pesce", "cervo", "cavallo", "capra", "pecora",
        "leone", "tigre", "zebra", "aquila", "falco", "serpente", "vipera", "gabbiano", "anatra",
        "passero", "coniglio", "castoro", "bruco", "cigno", "asino", "alpaca",
    },
    "plants": {
        "albero", "pianta", "fiore", "foglia", "radice", "seme", "bosco", "selva", "ulivo", "quercia",
        "ortica", "edera", "aloe", "tulipano", "spiga", "polline", "grano", "erba",
    },
    "nature": {
        "natura", "bosco", "selva", "montagna", "collina", "roccia", "pietra", "fiume", "lago", "mare",
        "riva", "fonte", "onda", "vento", "fuoco", "terra", "sole", "luna", "aurora", "nuvola",
        "nebbia", "deserto", "isola", "greto", "radice", "fiore", "foglia", "erba", "zolla",
    },
    "ecology": {
        "ambiente", "ecologia", "natura", "bosco", "energia", "acqua", "terra", "clima", "sorgere",
        "fonte", "solare", "verde", "ulivo", "pianta", "polline", "grano", "radice",
    },
    "geography": {
        "montagna", "collina", "isola", "deserto", "equatore", "ovest", "oriente", "riva", "mare",
        "lago", "fiume", "ponte", "confine", "quota", "pianeta", "roccia", "greto",
    },
    "weather": {
        "vento", "nebbia", "aurora", "pioggia", "sole", "nuvola", "tempesta", "brina", "sereno",
        "clima", "goccia",
    },
    "sea": {
        "mare", "onda", "vela", "barca", "porto", "pesce", "ancora", "scoglio", "riva", "veliero",
    },
    "mountain": {
        "montagna", "quota", "vetta", "roccia", "greto", "collina", "sentiero", "alpino",
    },
    "health": {
        "salute", "febbre", "medico", "cura", "respiro", "diuretico", "anemico", "vigore", "energia",
        "dente", "cuore", "corpo", "viso",
    },
    "science": {
        "atomo", "energia", "metodo", "equatore", "digitale", "misura", "tecnica", "triangolo",
        "microfibra", "microscopio", "algoritmo", "motore", "materia", "liquido",
    },
    "sport": {
        "calcio", "atleta", "sportivo", "gol", "pallone", "gara", "trionfo", "primato", "allenatore",
        "stadio", "squadra", "rete",
    },
    "history": {
        "re", "principe", "regno", "impero", "senato", "console", "legione", "vittoria", "epoca",
        "origine", "ritorno",
    },
    "school": {
        "libro", "quaderno", "lezione", "classe", "studiare", "maestro", "scuola", "esame", "penna",
        "aula", "figura", "titolo",
    },
    "cinema": {
        "film", "teatro", "attore", "scena", "dialogo", "regista", "pellicola", "cinema",
        "doppiatore", "documentario", "cinegiornale", "colossal", "commedia", "comparsa",
        "controfigura", "diva", "divo", "cabaret", "cartoon",
    },
    "literature": {
        "libro", "poesia", "favola", "fiaba", "frase", "parola", "lettura", "autore", "storia",
        "leggenda", "scrivere", "titolo",
    },
    "food": {
        "pane", "cacao", "gelato", "burro", "latte", "mandorla", "nocciola", "cena", "pranzo",
        "zuppa", "zucchero", "acqua", "fiore", "frutto",
    },
    "city": {
        "porta", "strada", "piazza", "ponte", "palazzo", "cortile", "villaggio", "citta", "urbano",
        "casale", "balcone", "finestra", "stazione",
    },
    "transport": {
        "automobile", "auto", "automezzo", "autoveicolo", "autovettura", "autobus", "autocarro",
        "aeromobile", "aeroplano", "aeroporto", "ambulanza", "autoambulanza", "astronave",
        "barca", "barchetta", "bastimento", "bicicletta", "bici", "bimotore", "bireattore",
        "bombardiere", "imbarcazione", "motrice", "motore", "nave", "pista", "porto",
        "quadrimotore", "reattore", "rimorchio", "rimorchiatore", "rotaia", "ruota", "trattore",
        "treno", "vapore", "vela", "veliero", "vettura", "volante", "volo",
    },
    "work": {
        "lavoro", "opera", "progetto", "metodo", "tecnica", "strumento", "martello", "guida",
        "mestiere", "servire",
    },
    "home": {
        "casa", "finestra", "porta", "parete", "divano", "tavolo", "sedia", "camera", "balcone",
        "camino", "tetto", "cortile", "vasca",
    },
}

TOPIC_SUFFIXES = {
    "actions": ("are", "ere", "ire"),
    "abstract": ("zione", "zioni", "ismo", "ezza", "ita", "mento", "anza", "enza"),
}


def infer_pos(tags: List[str]) -> str:
    for tag in tags:
        if tag in POS_BY_TAG:
            return POS_BY_TAG[tag]
    return "NOUN"


def infer_topics(word: str, tags: List[str]) -> List[str]:
    topics = {"general"}

    if "verb_infinitive" in tags:
        topics.add("actions")
    if any(word.endswith(suffix) for suffix in ("zione", "zioni", "ismo", "ezza", "ita", "mento", "anza", "enza")):
        topics.add("abstract")

    for topic, keywords in TOPIC_KEYWORDS.items():
        if word in keywords:
            topics.add(topic)

    for topic, suffixes in TOPIC_SUFFIXES.items():
        if any(word.endswith(suffix) for suffix in suffixes):
            topics.add(topic)

    if "animals" in topics:
        topics.add("nature")
    if "plants" in topics:
        topics.update({"nature", "ecology"})
    if "sea" in topics or "mountain" in topics or "weather" in topics:
        topics.add("nature")
    if "geography" in topics and "nature" not in topics:
        topics.add("nature")

    return sorted(topics)


def infer_register(quality: int) -> str:
    for threshold, label in REGISTER_BY_QUALITY:
        if quality >= threshold:
            return label
    return "rare"


def frequency_from_quality(quality: int, index: int, total: int) -> tuple[int, float]:
    rank = index + 1
    normalized_rank = 1.0 - (rank - 1) / max(1, total - 1)
    quality_boost = min(max(quality, 0), 10) / 20.0
    frequency_score = round(min(1.0, normalized_rank * 0.7 + quality_boost), 4)
    return rank, frequency_score


def load_words() -> List[str]:
    if not FILTERED_OUTPUT_PATH.exists() or not METADATA_OUTPUT_PATH.exists():
        build_vocabulary()

    words = [
        line.strip()
        for line in FILTERED_OUTPUT_PATH.read_text(encoding="utf-8").splitlines()
        if line.strip()
    ]
    return words


def load_metadata() -> Dict[str, Dict[str, object]]:
    if not METADATA_OUTPUT_PATH.exists():
        build_vocabulary()
    return json.loads(METADATA_OUTPUT_PATH.read_text(encoding="utf-8"))


def build_lexicon() -> Dict[str, object]:
    words = load_words()
    metadata = load_metadata()

    entries = []
    total = len(words)
    for index, word in enumerate(words):
        meta = metadata.get(word, {})
        tags = list(meta.get("tags", []))
        quality = int(meta.get("quality", 0))
        frequency_rank, frequency_score = frequency_from_quality(quality, index, total)

        entry = {
            "form": word,
            "normalized_form": word,
            "lemma": word,
            "pos": infer_pos(tags),
            "length": len(word),
            "frequency_rank": frequency_rank,
            "frequency_score": frequency_score,
            "difficulty_word": max(1, min(5, 6 - max(1, min(5, quality // 2 + 1)))),
            "allowed_in_crossword": True,
            "quality_score": max(0, min(10, quality)),
            "topics": infer_topics(word, tags),
            "morph_features": {},
            "register": infer_register(quality),
            "source_flags": ["from_filtered_vocabulary", "from_metadata_heuristics"],
            "crossword_flags": tags,
            "notes": "",
        }
        entries.append(entry)

    return {
        "meta": {
            "language": "it",
            "version": 1,
            "sources": ["vocaboli_it_filtrato.txt", "vocaboli_it_metadata.json"],
            "generated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
            "entry_count": len(entries),
        },
        "entries": entries,
    }


def main() -> None:
    lexicon = build_lexicon()
    LEXICON_OUTPUT_PATH.write_text(
        json.dumps(lexicon, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )
    print(f"Lessico generato: {LEXICON_OUTPUT_PATH}")
    print(f"Voci generate: {lexicon['meta']['entry_count']}")


if __name__ == "__main__":
    main()
