from __future__ import annotations

import copy
import json
from typing import Any

from guests.guest_defaults import (
    apply_istat_guest_defaults,
    is_missing_istat_value,
)
from istat.xml_export.exceptions import XmlPayloadValidationError
from istat.xml_export.services.lookup_service import IstatLookupService
from services.country_utils import normalize_country


COUNTRY_FIELDS = ("country_of_birth", "nationality", "country")


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


def _first_present(*values: Any) -> Any:
    for value in values:
        if not is_missing_istat_value(value):
            return value.strip() if isinstance(value, str) else value
    return None


def _normalize_country_code(value: Any) -> str | None:
    normalized = normalize_country(value)
    if is_missing_istat_value(normalized):
        return None
    return str(normalized).strip().upper()


def _normalize_guest_country_fields(guest: Any) -> Any:
    extra_data = _coerce_extra_data(getattr(guest, "extra_data", None))
    for field in COUNTRY_FIELDS:
        value = _first_present(getattr(guest, field, None), extra_data.get(field))
        setattr(guest, field, _normalize_country_code(value))
    return guest


def _require_guest_value(guest: Any, field_name: str) -> Any:
    extra_data = _coerce_extra_data(getattr(guest, "extra_data", None))
    value = _first_present(getattr(guest, field_name, None), extra_data.get(field_name))
    if is_missing_istat_value(value):
        raise XmlPayloadValidationError(
            f"{field_name} is required for ISTAT XML payload"
        )
    return value


def _require_country_mapping(guest: Any, field_name: str) -> str:
    value = _require_guest_value(guest, field_name)
    country_code = _normalize_country_code(value)
    if not country_code or IstatLookupService.get_country(country_code) is None:
        raise XmlPayloadValidationError(
            f"Missing ISTAT country mapping for '{country_code or ''}'"
        )
    setattr(guest, field_name, country_code)
    return country_code


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


def validate_guest_for_istat_export(guest: Any) -> Any:
    _require_guest_value(guest, "gender")
    _require_guest_value(guest, "date_of_birth")
    _require_country_mapping(guest, "country_of_birth")
    _require_country_mapping(guest, "nationality")
    _require_country_mapping(guest, "country")
    return guest
