# guests/models.py - Add new fields but keep id_number
import uuid

from django.db import models

from .istat_utils import ISTAT_GUEST_TYPE_CHOICES


def generate_idswh() -> str:
    """Generate a permanent regulatory identity token.

    Returns a 32-character hex string derived from a random UUID v4.
    This function is the ONLY authorised source of idswh values.
    It must never be called outside of GuestStay.save().
    """
    return uuid.uuid4().hex


class GuestStay(models.Model):
    """Canonical regulatory identity root for a single guest stay.

    This model is the ONLY source of truth for cross-system identity.
    It represents one physical guest stay (check-in → check-out lifecycle)
    and one regulatory reporting unit across all export systems:
    C59 / ISTAT / Ross 1000 / Alloggiati Web.

    Identity rules (NON-NEGOTIABLE):
    - ``idswh`` is generated once at creation and never recomputed.
    - No external system, serializer, or export layer may set or mutate it.
    - Re-exporting old data must always read the stored value, never derive a new one.
    """

    # ── Core relationships ────────────────────────────────────────────────────
    booking = models.ForeignKey(
        "bookings.Booking",
        on_delete=models.CASCADE,
        related_name="guest_stays",
        help_text="The booking this stay belongs to.",
    )
    guest = models.ForeignKey(
        "guests.Guest",
        on_delete=models.CASCADE,
        related_name="stays",
        help_text="The guest this stay belongs to.",
    )

    # ── Stay dates (denormalised for query efficiency) ────────────────────────
    check_in_date = models.DateField(
        help_text="Check-in date copied from the booking at creation time.",
    )
    check_out_date = models.DateField(
        help_text="Check-out date copied from the booking at creation time.",
    )

    # ── Regulatory identity ───────────────────────────────────────────────────
    idswh = models.CharField(
        max_length=64,
        unique=True,
        editable=False,
        null=True,          # null=True during Phase 1 rollout; enforced in Phase 3
        db_index=True,
        help_text=(
            "Permanent, globally unique regulatory identity token. "
            "Generated once at creation. Never recomputed, never overwritten. "
            "Used as the canonical identifier across C59, ISTAT, Ross 1000, "
            "and Alloggiati Web exports."
        ),
    )

    # ── Timestamps ────────────────────────────────────────────────────────────
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "guest_stays"
        indexes = [
            models.Index(fields=["booking", "guest"], name="gueststay_booking_guest_idx"),
            models.Index(fields=["check_in_date", "check_out_date"], name="gueststay_dates_idx"),
        ]

    def __str__(self) -> str:
        return f"GuestStay(id={self.pk}, idswh={self.idswh}, guest_id={self.guest_id})"

    def save(self, *args, **kwargs) -> None:
        """Assign idswh exactly once at creation.

        The guard ``if not self.idswh`` ensures:
        - New records receive a fresh token automatically.
        - Existing records (updates) are never touched.
        - No external caller can inject or overwrite the value because
          ``editable=False`` blocks form/serializer assignment.
        """
        if not self.idswh:
            self.idswh = generate_idswh()
        super().save(*args, **kwargs)

class Guest(models.Model):
    GENDER_CHOICES = [
        ('male', 'Male'),
        ('female', 'Female'),
        ('other', 'Other'),
    ]
    
    DOCUMENT_TYPE_CHOICES = [
        ('passport', 'Passport'),
        ('id_card', 'ID Card'),
        ('drivers_license', 'Driver\'s License'),
    ]
    
    booking = models.ForeignKey(
        "bookings.Booking",
        on_delete=models.CASCADE,
        related_name="guests",
        null=True,
        blank=False
    )
    
    # Basic Information
    full_name = models.CharField(max_length=255)
    is_main_guest = models.BooleanField(default=False)
    guest_type = models.CharField(
        max_length=2,
        blank=True,
        null=True,
        choices=ISTAT_GUEST_TYPE_CHOICES,
        help_text="ISTAT lodging type code for the guest record",
    )
    
    # Contact Information
    email = models.EmailField(blank=True, null=True)
    phone = models.CharField(max_length=20, blank=True, null=True)
    
    # NEW: Birth Information (for check-in form)
    date_of_birth = models.DateField(blank=True, null=True)
    country_of_birth = models.CharField(max_length=100, blank=True, null=True)
    city = models.CharField(max_length=100, blank=True, null=True)
    region = models.CharField(max_length=100, blank=True, null=True)
    gender = models.CharField(max_length=20, choices=GENDER_CHOICES, blank=True, null=True)
    tourism_type = models.CharField(
        max_length=64,
        blank=True,
        null=True,
        help_text="ISTAT tourism type associated with the guest",
    )
    transport_type = models.CharField(
        max_length=64,
        blank=True,
        null=True,
        help_text="ISTAT transport type associated with the guest",
    )
    
    # NEW: Document Information (for check-in form)
    document_type = models.CharField(max_length=50, choices=DOCUMENT_TYPE_CHOICES, blank=True, null=True)
    id_number = models.CharField(max_length=50, blank=True, null=True)  # KEEP THIS - old field
    document_issue_date = models.DateField(blank=True, null=True)  # NEW
    document_expiry_date = models.DateField(blank=True, null=True)  # NEW
    document_issuing_country = models.CharField(max_length=100, blank=True, null=True)  # NEW
    
    # Address & Identification
    nationality = models.CharField(max_length=100, blank=True, null=True)
    address = models.CharField(max_length=255, blank=True, null=True)
    zip_code = models.CharField(max_length=20, blank=True, null=True)
    country = models.CharField(max_length=100, blank=True, null=True)
    
    # Preferences & Notes
    language_preference = models.CharField(max_length=50, blank=True, null=True)
    special_requests = models.TextField(blank=True, null=True)
    guest_notes = models.TextField(blank=True, null=True)
    extra_data = models.JSONField(
        blank=True,
        null=True,
        default=dict,
        help_text="Store template-driven additional/custom fields for this guest (keyed by field slug)."
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    # ==========================
    # CITY TAX EXEMPTION FIELDS
    # ==========================

    is_city_tax_exempt = models.BooleanField(
        default=False,
        help_text="If true, guest is fully exempt from city tax"
    )

    city_tax_exemption_reason = models.CharField(
        max_length=50,
        null=True,
        blank=True,
        help_text="Reason for exemption (Medical, Student, Resident, etc.)"
    )

    city_tax_exemption_notes = models.TextField(
        null=True,
        blank=True,
        help_text="Internal notes for audit or verification"
    )

    class Meta:
        db_table = "guests"
        ordering = ["-created_at"]

    def __str__(self):
        return f"{self.full_name} - {'Main' if self.is_main_guest else 'Additional'} Guest"

    def save(self, *args, **kwargs):
        from guests.guest_defaults import apply_istat_guest_defaults

        apply_istat_guest_defaults(self)
        update_fields = kwargs.get("update_fields")
        if update_fields is not None:
            kwargs["update_fields"] = set(update_fields).union(
                {
                    "country_of_birth",
                    "nationality",
                    "country",
                    "tourism_type",
                    "transport_type",
                }
            )
        super().save(*args, **kwargs)
