from __future__ import annotations

import copy
import json
from collections.abc import Mapping
from typing import Any


ISTAT_NOT_SPECIFIED = "not_specified"
ISTAT_NOT_SPECIFIED_DISPLAY = "Not Specified"

ISTAT_NOT_SPECIFIED_ALIASES = {
    "not_specified",
    "not specified",
    "not-specified",
    "non specificato",
}


def is_missing_istat_value(value: Any) -> bool:
    if value is None:
        return True
    if isinstance(value, str):
        return not value.strip()
    return False


def _clean_value(value: Any) -> Any:
    if isinstance(value, str):
        return value.strip() or None
    return value


def _coerce_extra_data(value: Any) -> dict:
    if isinstance(value, dict):
        return value.copy()
    if isinstance(value, str):
        raw = value.strip()
        if not raw:
            return {}
        try:
            parsed = json.loads(raw)
        except (TypeError, ValueError, json.JSONDecodeError):
            return {}
        return parsed.copy() if isinstance(parsed, dict) else {}
    return {}


def _first_present(*values: Any) -> Any:
    for value in values:
        cleaned = _clean_value(value)
        if not is_missing_istat_value(cleaned):
            return cleaned
    return None


def normalize_istat_guest_defaults(
    values: Mapping[str, Any] | None,
    *,
    extra_data: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
    normalized = dict(values or {})
    extra = dict(extra_data or {})

    for field in (
        "country_of_birth",
        "nationality",
        "country",
        "tourism_type",
        "transport_type",
    ):
        if field in normalized:
            normalized[field] = _clean_value(normalized[field])

    country_of_birth = _first_present(
        normalized.get("country_of_birth"),
        extra.get("country_of_birth"),
    )
    nationality = _first_present(
        normalized.get("nationality"),
        extra.get("nationality"),
    )

    if is_missing_istat_value(normalized.get("nationality")):
        nationality = _first_present(nationality, country_of_birth)
        if nationality:
            normalized["nationality"] = nationality

    if is_missing_istat_value(normalized.get("country")):
        residence_country = _first_present(
            extra.get("country"),
            country_of_birth,
            nationality,
        )
        if residence_country:
            normalized["country"] = residence_country

    if is_missing_istat_value(normalized.get("tourism_type")):
        normalized["tourism_type"] = _first_present(
            extra.get("tourism_type"),
            ISTAT_NOT_SPECIFIED,
        )

    if is_missing_istat_value(normalized.get("transport_type")):
        normalized["transport_type"] = _first_present(
            extra.get("transport_type"),
            ISTAT_NOT_SPECIFIED,
        )

    return normalized


def apply_istat_guest_defaults(guest: Any) -> Any:
    extra_data = _coerce_extra_data(getattr(guest, "extra_data", None))
    values = {
        "country_of_birth": getattr(guest, "country_of_birth", None),
        "nationality": getattr(guest, "nationality", None),
        "country": getattr(guest, "country", None),
        "tourism_type": getattr(guest, "tourism_type", None),
        "transport_type": getattr(guest, "transport_type", None),
    }
    normalized = normalize_istat_guest_defaults(values, extra_data=extra_data)
    for field, value in normalized.items():
        setattr(guest, field, value)
    return guest


def normalize_guest_for_istat(guest: Any) -> Any:
    normalized_guest = copy.copy(guest)
    return apply_istat_guest_defaults(normalized_guest)


def istat_not_specified_export_value(value: Any) -> Any:
    if not isinstance(value, str):
        return value
    if value.strip().lower() in ISTAT_NOT_SPECIFIED_ALIASES:
        return ISTAT_NOT_SPECIFIED_DISPLAY
    return value
