"""Normalization helpers for provisional server collection flows."""

from __future__ import annotations

from typing import TYPE_CHECKING
from typing import Mapping

if TYPE_CHECKING:
    from .a2s_client import A2SServerInfo


MAP_NAME_ALIASES = {
    "stmarie": "St. Marie Du Mont",
    "stmariedumont": "St. Marie Du Mont",
    "saintemariedumont": "St. Marie Du Mont",
    "saintemariedumontwarfare": "St. Marie Du Mont",
    "saintemariedumontoffensiveus": "St. Marie Du Mont",
    "saintemariedumontoffensiveger": "St. Marie Du Mont",
    "saintemariedumontnight": "St. Marie Du Mont",
    "saintemariedumontovercast": "St. Marie Du Mont",
    "sainte-mariedumont": "St. Marie Du Mont",
    "sainte-marie-du-mont": "St. Marie Du Mont",
    "stmereeglise": "St. Mere Eglise",
    "stmereeglisewarfare": "St. Mere Eglise",
    "stmereegliseoffensiveus": "St. Mere Eglise",
    "stmereegliseoffensiveger": "St. Mere Eglise",
    "saintemereeglise": "St. Mere Eglise",
    "sainte-mere-eglise": "St. Mere Eglise",
    "purpleheartlane": "Purple Heart Lane",
    "utahbeach": "Utah Beach",
    "omahabeach": "Omaha Beach",
    "hurtgenforest": "Hurtgen Forest",
    "hill400": "Hill 400",
    "foy": "Foy",
    "kursk": "Kursk",
    "kharkov": "Kharkov",
    "kharkiv": "Kharkiv",
    "stalingrad": "Stalingrad",
    "remagen": "Remagen",
    "driel": "Driel",
    "elalamein": "El Alamein",
    "mortain": "Mortain",
    "carentan": "Carentan",
    "devn": "Elsenborn Ridge",
    "elsenbornridge": "Elsenborn Ridge",
    "elsenborn": "Elsenborn Ridge",
    "smolensk": "Smolensk",
    "smolenskwarfare": "Smolensk",
    "smolenskoffensiverus": "Smolensk",
    "smolenskoffensiveger": "Smolensk",
    "developertestmap": "Smolensk",
    "devq": "Smolensk",
}


def normalize_server_record(
    raw_record: Mapping[str, object],
    *,
    source_name: str,
) -> dict[str, object]:
    """Normalize a raw server record into the collector's internal shape."""
    external_server_id = _string_or_none(raw_record.get("external_server_id"))
    return {
        "external_server_id": external_server_id,
        "server_name": _string_or_default(raw_record.get("server_name"), "Unknown server"),
        "status": _normalize_status(raw_record.get("status")),
        "players": _coerce_int(raw_record.get("players")),
        "max_players": _coerce_int(raw_record.get("max_players")),
        "current_map": normalize_map_name(raw_record.get("current_map")),
        "region": _string_or_none(raw_record.get("region")),
        "source_name": source_name,
        "snapshot_origin": "controlled-fallback",
        "source_ref": external_server_id or source_name,
    }


def normalize_a2s_server_info(
    server_info: "A2SServerInfo",
    *,
    source_name: str,
    external_server_id: str | None = None,
    region: str | None = None,
) -> dict[str, object]:
    """Normalize a probed A2S payload into the collector's internal shape."""
    resolved_external_id = external_server_id or (
        f"a2s:{server_info.host}:{server_info.query_port}"
    )
    return {
        "external_server_id": resolved_external_id,
        "server_name": server_info.server_name or "Unknown server",
        "status": "online",
        "players": server_info.players,
        "max_players": server_info.max_players,
        "current_map": normalize_map_name(server_info.map_name),
        "region": region,
        "source_name": source_name,
        "snapshot_origin": "real-a2s",
        "source_ref": f"a2s://{server_info.host}:{server_info.query_port}",
    }


def normalize_map_name(value: object) -> str | None:
    """Normalize internal or abbreviated HLL map labels into a stable display name."""
    normalized = _string_or_none(value)
    if normalized is None:
        return None

    alias_key = "".join(character.lower() for character in normalized if character.isalnum())
    alias_match = MAP_NAME_ALIASES.get(alias_key)
    if alias_match:
        return alias_match

    for candidate_key, candidate_label in MAP_NAME_ALIASES.items():
        if alias_key.startswith(candidate_key):
            return candidate_label

    prettified = _prettify_map_name(normalized)
    return prettified or normalized


def _normalize_status(value: object) -> str:
    if not isinstance(value, str):
        return "unknown"

    normalized = value.strip().lower()
    if normalized in {"online", "offline", "unknown"}:
        return normalized

    return "unknown"


def _coerce_int(value: object) -> int | None:
    if value is None:
        return None

    try:
        return int(value)
    except (TypeError, ValueError):
        return None


def _string_or_none(value: object) -> str | None:
    if not isinstance(value, str):
        return None

    stripped = value.strip()
    return stripped or None


def _string_or_default(value: object, default: str) -> str:
    normalized = _string_or_none(value)
    return normalized or default


def _prettify_map_name(value: str) -> str:
    text = value.replace("_", " ").replace("-", " ").strip()
    compact_text = " ".join(text.split())
    if not compact_text:
        return value

    return " ".join(
        word.upper() if word.isdigit() else word.capitalize()
        for word in compact_text.split(" ")
    )
