"""Tests for Ross1000 guest payload resolver."""

from __future__ import annotations

from datetime import date
from unittest.mock import MagicMock, patch

import pytest

from istat.ross1000.exceptions import Ross1000GuestError, Ross1000ValidationError
from istat.ross1000.models.movement_payload import Ross1000GuestPayload
from istat.ross1000.services.guest_resolver import (
    _format_date_aaaammgg,
    _normalize_name_part,
    build_ross1000_guest_payload,
)


# ---------------------------------------------------------------------------
# _normalize_name_part()
# ---------------------------------------------------------------------------

class TestNormalizeNamePart:
    def test_two_word_name_splits_correctly(self):
        cognome, nome = _normalize_name_part("ROSSI MARIO")
        assert cognome == "ROSSI"
        assert nome == "MARIO"

    def test_lowercase_is_uppercased(self):
        cognome, nome = _normalize_name_part("rossi mario")
        assert cognome == "ROSSI"
        assert nome == "MARIO"

    def test_single_word_name(self):
        cognome, nome = _normalize_name_part("MADONNA")
        assert cognome == "MADONNA"
        assert nome == ""

    def test_three_word_name_splits_on_first_space(self):
        cognome, nome = _normalize_name_part("DE LUCA ANNA")
        assert cognome == "DE"
        assert nome == "LUCA ANNA"

    def test_none_returns_empty_strings(self):
        cognome, nome = _normalize_name_part(None)
        assert cognome == ""
        assert nome == ""

    def test_whitespace_only_returns_empty_strings(self):
        cognome, nome = _normalize_name_part("   ")
        assert cognome == ""
        assert nome == ""


# ---------------------------------------------------------------------------
# _format_date_aaaammgg()
# ---------------------------------------------------------------------------

class TestFormatDateAaaammgg:
    def test_formats_correctly(self):
        assert _format_date_aaaammgg(date(1983, 2, 9)) == "19830209"

    def test_formats_with_leading_zeros(self):
        assert _format_date_aaaammgg(date(2000, 1, 1)) == "20000101"

    def test_raises_for_none(self):
        with pytest.raises(Ross1000ValidationError, match="date_of_birth"):
            _format_date_aaaammgg(None)

    def test_raises_for_non_date(self):
        with pytest.raises(Ross1000ValidationError):
            _format_date_aaaammgg("1983-02-09")  # type: ignore


# ---------------------------------------------------------------------------
# build_ross1000_guest_payload()
# ---------------------------------------------------------------------------

def _make_guest(**overrides):
    guest = MagicMock()
    guest.id = 1
    guest.full_name = "ROSSI MARIO"
    guest.date_of_birth = date(1983, 2, 9)
    guest.gender = "male"
    guest.country_of_birth = "IT"
    guest.country = "IT"
    guest.nationality = "IT"
    guest.extra_data = {}
    guest.tourism_type = None
    guest.transport_type = None
    guest.city = None
    guest.region = None
    guest.city_of_birth = None
    for key, value in overrides.items():
        setattr(guest, key, value)
    return guest


class TestBuildRoss1000GuestPayload:
    def test_returns_correct_payload_for_italian_guest(self):
        guest = _make_guest()
        payload = build_ross1000_guest_payload(
            guest=guest,
            booking_id=10,
            idswh="abc123",
        )
        assert isinstance(payload, Ross1000GuestPayload)
        assert payload.idswh == "abc123"
        assert payload.cognome == "ROSSI"
        assert payload.nome == "MARIO"
        assert payload.sesso == "M"
        assert payload.datanascita == "19830209"
        assert payload.statonascita == "100000100"
        assert payload.statoresidenza == "100000100"
        assert payload.cittadinanza == "100000100"

    def test_sesso_female_resolved_correctly(self):
        guest = _make_guest(gender="female")
        payload = build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="abc")
        assert payload.sesso == "F"

    def test_sesso_male_shorthand(self):
        guest = _make_guest(gender="m")
        payload = build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="abc")
        assert payload.sesso == "M"

    def test_raises_when_gender_missing(self):
        guest = _make_guest(gender="")
        with pytest.raises(Ross1000GuestError, match="gender"):
            build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="abc")

    def test_raises_when_gender_invalid(self):
        guest = _make_guest(gender="other")
        with pytest.raises(Ross1000GuestError, match="gender"):
            build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="abc")

    def test_returns_correct_payload_for_german_guest(self):
        guest = _make_guest(
            full_name="MÜLLER HANS",
            country_of_birth="DE",
            country="DE",
            nationality="DE",
        )
        payload = build_ross1000_guest_payload(
            guest=guest,
            booking_id=10,
            idswh="xyz789",
        )
        assert payload.statonascita == "200000004"
        assert payload.statoresidenza == "200000004"
        assert payload.cittadinanza == "200000004"

    def test_payload_is_frozen(self):
        guest = _make_guest()
        payload = build_ross1000_guest_payload(
            guest=guest,
            booking_id=10,
            idswh="abc123",
        )
        with pytest.raises((AttributeError, TypeError)):
            payload.idswh = "changed"  # type: ignore

    def test_raises_when_idswh_missing(self):
        guest = _make_guest()
        with pytest.raises(Ross1000ValidationError, match="idswh"):
            build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="")

    def test_raises_when_dob_missing(self):
        guest = _make_guest(date_of_birth=None)
        with pytest.raises((Ross1000GuestError, Ross1000ValidationError)):
            build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="abc")

    def test_raises_when_country_of_birth_unknown(self):
        guest = _make_guest(country_of_birth="ZZ")
        with pytest.raises(Ross1000GuestError, match="country_of_birth"):
            build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="abc")

    def test_date_formatted_as_aaaammgg(self):
        guest = _make_guest(date_of_birth=date(1990, 12, 31))
        payload = build_ross1000_guest_payload(
            guest=guest,
            booking_id=10,
            idswh="abc123",
        )
        assert payload.datanascita == "19901231"

    def test_name_is_uppercased(self):
        guest = _make_guest(full_name="bianchi lucia")
        payload = build_ross1000_guest_payload(
            guest=guest,
            booking_id=10,
            idswh="abc123",
        )
        assert payload.cognome == "BIANCHI"
        assert payload.nome == "LUCIA"

    def test_idswh_is_stripped(self):
        guest = _make_guest()
        payload = build_ross1000_guest_payload(
            guest=guest,
            booking_id=10,
            idswh="  abc123  ",
        )
        assert payload.idswh == "abc123"

    def test_optional_fields_none_when_not_set(self):
        guest = _make_guest()
        payload = build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="abc")
        assert payload.comune_residenza is None
        assert payload.provincia_residenza is None
        assert payload.comune_nascita is None

    def test_optional_fields_populated_when_present(self):
        guest = _make_guest(city="Roma", region="RM", city_of_birth="Napoli")
        payload = build_ross1000_guest_payload(guest=guest, booking_id=10, idswh="abc")
        assert payload.comune_residenza == "Roma"
        assert payload.provincia_residenza == "RM"
        assert payload.comune_nascita == "Napoli"
