from __future__ import annotations

import tempfile
import unittest
from datetime import date
from pathlib import Path

from bookings.models import Booking
from django.test import TestCase
from guests.models import Guest
from istat.models import IstatMunicipality
from istat.xml_export.application.export_facade import (
    generate_c59_xml_download,
    generate_guest_xml_download,
)
from istat.xml_export.exceptions import XmlPayloadValidationError
from properties.models import Property, PropertyType
from structures.models import Structure


MINIMAL_C59_XSD = """<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:rim="http://www.regione.liguria.it/turismo/rimovcli"
    targetNamespace="http://www.regione.liguria.it/turismo/rimovcli"
    elementFormDefault="qualified">
  <xs:element name="c59">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="mensile" minOccurs="1" maxOccurs="1"/>
        <xs:element name="giornaliero" minOccurs="1" maxOccurs="1"/>
      </xs:sequence>
      <xs:attribute name="idstruttura" type="xs:string" use="required"/>
      <xs:attribute name="data" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>
</xs:schema>
"""


LXML_AVAILABLE = True
try:
    import lxml.etree  # noqa: F401
except ModuleNotFoundError:
    LXML_AVAILABLE = False


class XmlExportEndToEndTests(TestCase):
    def setUp(self):
        from django.contrib.auth.models import User
        self.user = User.objects.create_user(username="e2euser", password="pass123")
        self.structure = Structure.objects.create(
            user=self.user,
            name="Hotel Liguria",
            structure_type="Hotel",
            zip_code="16121",
            country="Italy",
            istat_code="LIG001",
        )
        self.property_type = PropertyType.objects.create(
            structure=self.structure,
            name="Double Room",
            max_guests=2,
            num_beds=2,
        )
        self.property = Property.objects.create(
            structure=self.structure,
            property_type=self.property_type,
            name="Room 101",
        )
        IstatMunicipality.objects.create(
            code="015146000",
            name="Milano",
            province="MI",
            region="Lombardia",
        )

    def _create_booking_with_guest(self, *, adults_count=1):
        booking = Booking.objects.create(
            structure=self.structure,
            property_type=self.property_type,
            property=self.property,
            check_in_date=date(2026, 5, 13),
            check_out_date=date(2026, 5, 14),
            adults_count=adults_count,
            children_count=0,
            tourism_type="Business",
            transport_type="Car",
            is_checked_in=True,
        )
        Guest.objects.create(
            booking=booking,
            full_name="Mario Rossi",
            is_main_guest=True,
            guest_type="16",
            gender="male",
            date_of_birth=date(1990, 1, 1),
            country_of_birth="IT",
            nationality="IT",
            country="IT",
            city="Milano",
            extra_data={"province": "MI"},
        )
        return booking

    def test_guest_export_download_end_to_end(self):
        self._create_booking_with_guest()

        response = generate_guest_xml_download(
            structure_id=self.structure.id,
            start_date=date(2026, 5, 13),
            end_date=date(2026, 5, 13),
        )

        self.assertEqual(response["Content-Type"], "application/xml")
        self.assertIn("istat_movimenti_", response["Content-Disposition"])
        self.assertIn("<MOVIMENTO>", response.content.decode("utf-8"))
        self.assertTrue(response.content.decode("utf-8").startswith("<?xml"))

    def test_guest_export_download_fails_for_synthetic_guests(self):
        self._create_booking_with_guest(adults_count=2)

        with self.assertRaises(XmlPayloadValidationError):
            generate_guest_xml_download(
                structure_id=self.structure.id,
                start_date=date(2026, 5, 13),
                end_date=date(2026, 5, 13),
            )

    def test_c59_export_download_end_to_end(self):
        self._create_booking_with_guest()

        response = generate_c59_xml_download(
            structure_id=self.structure.id,
            report_date=date(2026, 5, 13),
        )

        self.assertEqual(response["Content-Type"], "application/xml")
        self.assertIn("istat_c59_", response["Content-Disposition"])
        body = response.content.decode("utf-8")
        self.assertIn('xmlns:rim="http://www.regione.liguria.it/turismo/rimovcli"', body)
        self.assertTrue(body.startswith("<?xml"))

    @unittest.skipUnless(LXML_AVAILABLE, "lxml is not installed")
    def test_c59_export_download_with_xsd_validation(self):
        self._create_booking_with_guest()
        with tempfile.NamedTemporaryFile("w", suffix=".xsd", delete=False) as handle:
            handle.write(MINIMAL_C59_XSD)
            xsd_path = handle.name

        self.addCleanup(lambda: Path(xsd_path).unlink(missing_ok=True))
        response = generate_c59_xml_download(
            structure_id=self.structure.id,
            report_date=date(2026, 5, 13),
            validate_xsd=True,
            xsd_path=xsd_path,
        )

        self.assertEqual(response.status_code, 200)

    def test_c59_export_download_fails_for_missing_istat_code(self):
        self._create_booking_with_guest()
        self.structure.istat_code = ""
        self.structure.save(update_fields=["istat_code"])

        with self.assertRaises(XmlPayloadValidationError):
            generate_c59_xml_download(
                structure_id=self.structure.id,
                report_date=date(2026, 5, 13),
            )

    def test_c59_export_download_fails_for_missing_xsd(self):
        self._create_booking_with_guest()

        with self.assertRaises(XmlPayloadValidationError):
            generate_c59_xml_download(
                structure_id=self.structure.id,
                report_date=date(2026, 5, 13),
                validate_xsd=True,
                xsd_path="/tmp/missing-c59-schema.xsd",
            )
