"""
alloggiati/models.py
====================
Persistence layer for Alloggiati Web sync operations.

No guest PII (names, document numbers) is ever stored — only
aggregated counts and status metadata.
"""

from __future__ import annotations

import uuid

from django.db import models


class AlloggiatiCredential(models.Model):
    """
    Stores encrypted Alloggiati Web credentials for a structure.

    Supports two authentication modes:
      - DIGITAL_CERTIFICATE: mTLS client certificate + private key
      - CODES: username / password (web service key)

    Sensitive fields are stored encrypted via istat.security helpers
    (Fernet symmetric encryption keyed from settings.SECRET_KEY or
    ISTAT_CREDENTIAL_ENCRYPTION_KEY).
    """

    MODE_DIGITAL_CERTIFICATE = "DIGITAL_CERTIFICATE"
    MODE_CODES = "CODES"

    MODE_CHOICES = [
        (MODE_DIGITAL_CERTIFICATE, "Digital Certificate (mTLS)"),
        (MODE_CODES, "Codes (Web Service Key)"),
    ]

    structure = models.OneToOneField(
        "structures.Structure",
        on_delete=models.CASCADE,
        related_name="alloggiati_credential",
    )
    mode = models.CharField(
        max_length=32,
        choices=MODE_CHOICES,
        default=MODE_CODES,
        help_text="Authentication mode for Alloggiati Web.",
    )

    # CODES mode (encrypted)
    username_encrypted = models.TextField(blank=True, default="")
    password_encrypted = models.TextField(blank=True, default="")

    # DIGITAL_CERTIFICATE mode (encrypted PEM blobs)
    certificate_encrypted = models.TextField(blank=True, default="")
    private_key_encrypted = models.TextField(blank=True, default="")

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "alloggiati_credentials"

    def __str__(self) -> str:
        return f"AlloggiatiCredential [{self.mode}] for {self.structure}"

    # ------------------------------------------------------------------
    # Encrypted property accessors — decrypt only in memory, never log
    # ------------------------------------------------------------------

    @property
    def username(self) -> str:
        from istat.security import decrypt_istat_secret
        return decrypt_istat_secret(self.username_encrypted)

    @username.setter
    def username(self, value: str) -> None:
        from istat.security import encrypt_istat_secret
        self.username_encrypted = encrypt_istat_secret(value)

    @property
    def password(self) -> str:
        from istat.security import decrypt_istat_secret
        return decrypt_istat_secret(self.password_encrypted)

    @password.setter
    def password(self, value: str) -> None:
        from istat.security import encrypt_istat_secret
        self.password_encrypted = encrypt_istat_secret(value)

    @property
    def certificate(self) -> str:
        from istat.security import decrypt_istat_secret
        return decrypt_istat_secret(self.certificate_encrypted)

    @certificate.setter
    def certificate(self, value: str) -> None:
        from istat.security import encrypt_istat_secret
        self.certificate_encrypted = encrypt_istat_secret(value)

    @property
    def private_key(self) -> str:
        from istat.security import decrypt_istat_secret
        return decrypt_istat_secret(self.private_key_encrypted)

    @private_key.setter
    def private_key(self, value: str) -> None:
        from istat.security import encrypt_istat_secret
        self.private_key_encrypted = encrypt_istat_secret(value)


class AlloggiatiSyncLog(models.Model):
    """
    Immutable audit record for a single Alloggiati Web sync attempt.

    One record is created per sync call regardless of outcome.
    No guest PII is stored — only aggregated counts and status.
    """

    class Status(models.TextChoices):
        CONNECTED = "CONNECTED", "Connected (all guests sent)"
        PARTIAL = "PARTIAL", "Partial (some guests rejected)"
        ERROR = "ERROR", "Error (sync failed)"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    structure = models.ForeignKey(
        "structures.Structure",
        on_delete=models.CASCADE,
        related_name="alloggiati_sync_logs",
    )

    # Sync window
    date_from = models.DateField()
    date_to = models.DateField()

    # Outcome
    status = models.CharField(
        max_length=16,
        choices=Status.choices,
        default=Status.ERROR,
    )
    message = models.TextField(blank=True, default="")

    # Aggregated counts — no PII
    guests_sent = models.PositiveIntegerField(default=0)
    guests_rejected = models.PositiveIntegerField(default=0)

    # Structured per-guest validation errors — persisted for audit trail,
    # frontend rendering, and support debugging.
    # Schema: [{"guest_id": int, "booking_id": int, "guest_name": str,
    #           "errors": [{"field": str, "message": str}, ...]}, ...]
    # No document numbers, credentials, or other PII are stored here.
    validation_errors = models.JSONField(default=list, blank=True)

    # Timing
    started_at = models.DateTimeField()
    completed_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        db_table = "alloggiati_sync_logs"
        ordering = ["-started_at"]

    def __str__(self) -> str:
        return (
            f"AlloggiatiSyncLog [{self.status}] "
            f"{self.structure} "
            f"{self.date_from}→{self.date_to}"
        )
