"""Resolve guest data into Ross1000 guest payloads.

Reuses the existing guest normalization pipeline (apply_istat_guest_defaults)
to fill missing fields, then maps to Ross1000-specific 9-digit country codes.

Does NOT touch the TXT export, C59, or Alloggiati pipelines.
"""

from __future__ import annotations

from typing import Any

from guests.guest_defaults import apply_istat_guest_defaults
from istat.ross1000.exceptions import Ross1000GuestError, Ross1000ValidationError
from istat.ross1000.models.country_codes import get_ross1000_country_code
from istat.ross1000.models.movement_payload import Ross1000GuestPayload
from istat.ross1000.validators.payload_validator import (
    validate_guest_for_ross1000,
    validate_idswh,
)
from services.country_utils import normalize_country


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------

def _normalize_name_part(value: str | None) -> tuple[str, str]:
    """Split full_name into (cognome, nome) — surname first, given name second.

    Convention: "ROSSI MARIO" → cognome="ROSSI", nome="MARIO"
    Single-word names → cognome=name, nome=""
    """
    raw = str(value or "").strip().upper()
    parts = raw.split(None, 1)
    if len(parts) == 2:
        return parts[0], parts[1]
    return raw, ""


def _format_date_aaaammgg(dob: Any) -> str:
    """Format a date as AAAAMMGG (e.g. 19830209)."""
    if dob is None:
        raise Ross1000ValidationError("date_of_birth is required for Ross1000 export")
    try:
        return dob.strftime("%Y%m%d")
    except AttributeError as exc:
        raise Ross1000ValidationError(
            f"date_of_birth must be a date instance, got {type(dob)}"
        ) from exc


def _resolve_gender(guest: Any, guest_id: int) -> str:
    """Resolve gender to Ross1000 sesso code: 'M' or 'F'.

    Accepts: 'male'/'M'/'m' → 'M', 'female'/'F'/'f' → 'F'.
    Raises Ross1000GuestError if the value is missing or unrecognised.
    """
    raw = str(getattr(guest, "gender", "") or "").strip().lower()
    if raw in ("male", "m", "1"):
        return "M"
    if raw in ("female", "f", "2"):
        return "F"
    raise Ross1000GuestError(
        f"[guest {guest_id}] gender is required and must be 'male' or 'female', "
        f"got '{raw or '(empty)'}'"
    )


def _resolve_country_code(value: str | None, field: str, guest_id: int) -> str:
    """Normalize ISO-2 and resolve to 9-digit Ross1000 code."""
    normalized = normalize_country(value)
    if not normalized:
        raise Ross1000GuestError(
            f"[guest {guest_id}] {field} is missing or unresolvable: '{value}'"
        )
    code = get_ross1000_country_code(normalized)
    if code is None:
        raise Ross1000GuestError(
            f"[guest {guest_id}] no Ross1000 country code for {field}='{normalized}'"
        )
    return code


def _optional_str(value: Any) -> str | None:
    """Return stripped string or None if blank."""
    if value is None:
        return None
    s = str(value).strip()
    return s if s else None


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

def build_ross1000_guest_payload(
    *,
    guest: Any,
    booking_id: int,
    idswh: str,
) -> Ross1000GuestPayload:
    """Build a validated Ross1000 guest payload from a Guest model instance.

    Steps:
    1. Apply ISTAT guest defaults (fills missing country/nationality from extra_data)
    2. Validate all required fields
    3. Resolve gender → sesso code
    4. Map to 9-digit ISTAT country codes
    5. Collect optional municipality fields
    6. Return immutable payload

    Args:
        guest:      Guest model instance (already prefetched)
        booking_id: Parent booking ID (for error context)
        idswh:      Permanent regulatory identity token from GuestStay

    Returns:
        Validated, immutable Ross1000GuestPayload

    Raises:
        Ross1000GuestError:      On missing/invalid guest fields
        Ross1000ValidationError: On missing idswh or country mapping
    """
    # Apply defaults (fills nationality from country_of_birth, etc.)
    apply_istat_guest_defaults(guest)

    guest_id = getattr(guest, "id", 0) or 0

    # Validate idswh — must already exist, never generated here
    validated_idswh = validate_idswh(idswh, guest_id=guest_id, booking_id=booking_id)

    # Validate all required fields (raises on first failure)
    validate_guest_for_ross1000(guest, booking_id=booking_id)

    # Split name into cognome / nome
    cognome, nome = _normalize_name_part(getattr(guest, "full_name", None))

    # Gender → sesso
    sesso = _resolve_gender(guest, guest_id)

    # Date of birth → AAAAMMGG
    datanascita = _format_date_aaaammgg(getattr(guest, "date_of_birth", None))

    # Mandatory 9-digit country codes
    statonascita = _resolve_country_code(
        getattr(guest, "country_of_birth", None), "country_of_birth", guest_id
    )
    statoresidenza = _resolve_country_code(
        getattr(guest, "country", None), "country", guest_id
    )
    cittadinanza = _resolve_country_code(
        getattr(guest, "nationality", None), "nationality", guest_id
    )

    # Optional municipality fields (present on Italian guests)
    comune_residenza = _optional_str(getattr(guest, "city", None))
    provincia_residenza = _optional_str(getattr(guest, "region", None))
    comune_nascita = _optional_str(getattr(guest, "city_of_birth", None))

    return Ross1000GuestPayload(
        idswh=validated_idswh,
        cognome=cognome,
        nome=nome,
        sesso=sesso,
        datanascita=datanascita,
        cittadinanza=cittadinanza,
        statoresidenza=statoresidenza,
        statonascita=statonascita,
        comune_residenza=comune_residenza,
        provincia_residenza=provincia_residenza,
        comune_nascita=comune_nascita,
    )
