"""Proposition automatique de mapping pour `generic_csv` (spec Imports §5, décision 2026-09-01).

« Le mapping, nous on le fait » : un export non reconnu ne doit jamais poser à l'utilisateur
un écran de neuf `Select` vides. L'heuristique confronte les en-têtes du fichier à une table
de synonymes par champ canonique — insensible à la casse, aux accents et à la ponctuation,
EN et FR — et propose ce qu'elle a trouvé. Ce qu'on éprouve ici :

- l'**export réel Mangools KWFinder** (`real/kwfinder_usa_top.csv`, 147 colonnes dont 133
  volumes mensuels historiques) est mappé **sans rien savoir de son format** : l'heuristique
  ne connaît aucune source, et ce fichier reste son banc d'essai le plus dur même depuis que
  l'adaptateur `mangools` (T7) le reconnaît — c'est le chemin qu'emprunte tout export d'un
  outil dont on n'a pas encore de signature ;
- la **série mensuelle ne gagne jamais** sur la moyenne 12 mois — le piège de ce fichier ;
- l'**ambiguïté est rendue**, pas tranchée en silence : la meilleure colonne est retenue et
  les autres sont nommées, pour que l'utilisateur puisse corriger d'un geste ;
- les **faux amis** documentés restent hors du mapping (`Competitive Density` n'est pas une
  position, `Content Type` n'est pas une intention) ;
- sans colonne mot-clé, la proposition est **inutilisable** et se dit telle : `generic_csv`
  exige ce champ, une proposition sans lui ferait un 422 à l'application.
"""

from pathlib import Path

from app.keywords.sources import suggest_columns
from app.keywords.sources.csvfile import read_header

KWFINDER = Path(__file__).parent / "fixtures" / "real" / "kwfinder_usa_top.csv"


# --- export réel Mangools KWFinder ---------------------------------------------------------


def test_real_kwfinder_export_is_mapped_by_synonyms_alone() -> None:
    suggestion = suggest_columns(read_header(KWFINDER))
    assert suggestion.usable
    assert suggestion.columns == {
        "keyword": "Keyword",
        "volume": "Avg. Search Volume (Last 12 months)",
        "kd": "Keyword Difficulty",
        "cpc": "CPC/USD",
        "intent": "Search Intent",
        "serp_features": "Serp Features",
    }


def test_a_single_growth_percentage_is_never_proposed_as_a_trend() -> None:
    """`Keyword interest growth (%)` est **une** valeur ; le canonique en attend douze.

    La mapper rendrait `trend=None` sur chaque ligne — une colonne vide que l'utilisateur ne
    peut pas corriger, faute d'une colonne correcte dans son fichier (arbitrage 2026-09-01).
    """
    suggestion = suggest_columns(read_header(KWFINDER))
    assert "trend" not in suggestion.columns
    assert "trend" not in suggestion.alternatives


def test_monthly_volume_series_never_beats_the_twelve_month_average() -> None:
    """133 colonnes `Search Volume 7/2015` : une seule d'entre elles ferait un volume faux."""
    suggestion = suggest_columns(read_header(KWFINDER))
    assert suggestion.columns["volume"] == "Avg. Search Volume (Last 12 months)"
    assert not any(
        header.startswith("Search Volume ") for header in suggestion.alternatives.get("volume", ())
    )


def test_kwfinder_ambiguity_names_the_other_volume_columns() -> None:
    """Les trois autres moyennes sont des candidates légitimes : elles sont **listées**."""
    suggestion = suggest_columns(read_header(KWFINDER))
    assert suggestion.alternatives["volume"] == [
        "Avg. Search Volume (Last Known Values)",
        "Avg. Search Volume (Last 6 months)",
        "Avg. Search Volume (Last 3 months)",
    ]


# --- ambiguïté ------------------------------------------------------------------------------


def test_ambiguity_keeps_the_best_and_reports_the_rest() -> None:
    suggestion = suggest_columns(["Keyword", "Prev. position", "Position", "Ranked URL"])
    assert suggestion.columns["position"] == "Position"
    assert suggestion.alternatives["position"] == ["Prev. position"]
    assert suggestion.columns["url"] == "Ranked URL"
    assert "url" not in suggestion.alternatives


def test_one_header_never_serves_two_fields() -> None:
    """`Keyword Difficulty` est une difficulté, pas un mot-clé : une colonne sert un seul champ."""
    suggestion = suggest_columns(["Keyword", "Keyword Difficulty"])
    assert suggestion.columns == {"keyword": "Keyword", "kd": "Keyword Difficulty"}


# --- faux amis ------------------------------------------------------------------------------


def test_competitive_density_is_never_a_position() -> None:
    suggestion = suggest_columns(["Keyword", "Volume", "Competitive Density", "Number of Results"])
    assert suggestion.columns == {"keyword": "Keyword", "volume": "Volume"}


def test_serp_position_is_never_a_serp_feature() -> None:
    """`to_serp_features("3")` ferait la fonctionnalité SERP « 3 » : une métrique fausse, et
    persistée — l'aperçu ne montre pas cette colonne, personne ne la verrait passer."""
    suggestion = suggest_columns(["Keyword", "SERP Position", "SERP rank"])
    assert suggestion.columns == {"keyword": "Keyword"}
    #  Une colonne qu'on ne sait pas nommer reste **absente** : l'utilisateur la câble lui-même
    #  s'il la veut, ce qui est toujours moins coûteux qu'une valeur fausse en base.
    assert suggestion.alternatives == {}


def test_content_type_is_never_an_intent() -> None:
    suggestion = suggest_columns(["Keyword", "Content Type", "PPC", "Keyword Opportunities"])
    assert suggestion.columns == {"keyword": "Keyword"}


# --- français et abréviations ----------------------------------------------------------------


def test_french_headers_are_recognised_accents_and_punctuation_aside() -> None:
    suggestion = suggest_columns(
        ["Mot-clé", "Volume de recherche", "Difficulté", "CPC (EUR)", "Intention", "Page"]
    )
    assert suggestion.columns == {
        "keyword": "Mot-clé",
        "volume": "Volume de recherche",
        "kd": "Difficulté",
        "cpc": "CPC (EUR)",
        "intent": "Intention",
        "url": "Page",
    }


def test_terse_headers_are_recognised() -> None:
    suggestion = suggest_columns(["kw", "msv", "SD", "cpc"])
    assert suggestion.columns == {"keyword": "kw", "volume": "msv", "kd": "SD", "cpc": "cpc"}


# --- proposition inutilisable ------------------------------------------------------------------


def test_without_a_keyword_column_the_suggestion_is_not_usable() -> None:
    """`generic_csv` exige `keyword` : proposer le reste ferait un 422 à l'application."""
    suggestion = suggest_columns(["Volume", "CPC (USD)", "Difficulté"])
    assert not suggestion.usable
    assert "keyword" not in suggestion.columns


def test_a_file_of_unknown_columns_suggests_nothing() -> None:
    suggestion = suggest_columns(["A", "B", "C"])
    assert suggestion.columns == {}
    assert not suggestion.usable
