"""
Tests du parseur de localisations Metal Archives.

Aucun accès réseau ni base : `parser.py` n'a que des fonctions pures, ce qui en
fait le module le plus rentable à couvrir de tout le pipeline de géocodage —
c'est lui qui décide combien de lignes `band_locations` sont créées, et donc
combien d'appels Geoapify/Groq (payants) seront déclenchés.
"""

import pytest
from src.parser import (
    _classify,
    build_fallback_queries,
    country_centroid,
    parse_location_text,
)


class TestClassify:
    @pytest.mark.parametrize("value", ["N/A", "n/a", "", "   "])
    def test_valeurs_vides_ignorees(self, value):
        assert _classify(value) == "skip"

    @pytest.mark.parametrize("value", ["FR", "DE", "NO"])
    def test_codes_iso2(self, value):
        assert _classify(value) == "country_code"

    def test_ville_normale(self):
        assert _classify("Oslo") == "parseable"


class TestParseLocationText:
    def test_texte_vide_ne_produit_aucune_ligne(self):
        assert parse_location_text("") == []
        assert parse_location_text("N/A") == []

    def test_ville_simple(self):
        rows = parse_location_text("Oslo")
        assert len(rows) == 1
        assert rows[0]["location_raw"] == "Oslo"
        assert rows[0]["is_country_only"] is False
        assert rows[0]["step_order"] == 0

    def test_ville_avec_region(self):
        rows = parse_location_text("Bergen, Hordaland")
        assert [r["location_raw"] for r in rows] == ["Bergen, Hordaland"]

    def test_code_pays_seul_marque_country_only(self):
        rows = parse_location_text("FR")
        assert len(rows) == 1
        assert rows[0]["is_country_only"] is True
        assert rows[0]["location_raw"] == "FR"

    def test_code_pays_minuscule_non_reconnu(self):
        """Comportement actuel documenté, pas une validation.

        `_ISO2_RE` vaut `^[A-Z]{2}$` : un code pays en minuscules n'est PAS
        reconnu comme pays et repart en géocodage comme s'il s'agissait d'une
        ville, ce qui consomme un appel Geoapify pour un résultat au mieux
        douteux. Metal Archives renvoie les codes en majuscules, donc le cas ne
        se produit pas en pratique aujourd'hui — mais rien ne le garantit.
        Si `_ISO2_RE` devient insensible à la casse, ce test doit être inversé.
        """
        rows = parse_location_text("fr")
        assert rows[0]["is_country_only"] is False

    def test_plusieurs_villes_separees(self):
        """Une même étape peut lister plusieurs villes : chacune devient une ligne."""
        rows = parse_location_text("Oslo/Bergen")
        assert len(rows) >= 2
        assert {r["location_raw"] for r in rows} >= {"Oslo", "Bergen"}
        # Même étape → même step_order
        assert len({r["step_order"] for r in rows}) == 1

    def test_etapes_successives_incrementent_step_order(self):
        rows = parse_location_text("Oslo (early), Bergen (later)")
        orders = [r["step_order"] for r in rows]
        assert orders == sorted(orders)

    def test_les_valeurs_ignorables_ne_creent_pas_de_ligne(self):
        rows = parse_location_text("N/A")
        assert rows == []

    def test_resultat_toujours_serialisable(self):
        """Chaque ligne doit exposer exactement les clés attendues par db.py."""
        for row in parse_location_text("Trondheim (1991-1993), Oslo"):
            assert set(row) == {"step_order", "step_label", "location_raw", "is_country_only"}
            assert isinstance(row["step_order"], int)
            assert isinstance(row["is_country_only"], bool)


class TestCountryCentroid:
    def test_code_iso2_connu(self):
        lat, lon = country_centroid("FR")
        assert 41 < lat < 52
        assert -6 < lon < 10

    def test_insensible_a_la_casse_et_aux_espaces(self):
        assert country_centroid("  fr  ") == country_centroid("FR")

    def test_code_inconnu_renvoie_none(self):
        assert country_centroid("ZZ") is None


class TestBuildFallbackQueries:
    def test_du_plus_specifique_au_plus_vague(self):
        queries = build_fallback_queries("Bergen, Hordaland", "NO")
        assert queries[0].startswith("Bergen, Hordaland")
        assert queries[-1] == "Bergen"

    def test_aucun_doublon(self):
        queries = build_fallback_queries("Oslo", "NO")
        assert len(queries) == len(set(queries))

    def test_le_pays_est_injecte_comme_contexte(self):
        queries = build_fallback_queries("Bergen", "NO")
        assert any("Norway" in q for q in queries)

    def test_pays_deja_present_non_duplique(self):
        queries = build_fallback_queries("Bergen, Norway", "NO")
        assert not any(q.count("Norway") > 1 for q in queries)

    def test_pays_inconnu_ne_plante_pas(self):
        assert build_fallback_queries("Bergen", "ZZ") == ["Bergen"]
        assert build_fallback_queries("Bergen", None) == ["Bergen"]

    def test_toujours_au_moins_une_requete(self):
        for raw in ["Oslo", "A, B, C, D", "Saint-Étienne"]:
            assert len(build_fallback_queries(raw, "FR")) >= 1
