"""
Modelli Virtual Box – filtri nominati assegnabili agli utenti.

Struttura:
  VirtualBox         → definisce il filtro (nome, label, mailbox scope)
  VirtualBoxRule     → singola regola (field + pattern) dentro una VBox
  VirtualBoxAssignment → assegnazione VBox → User

Logica:
  - Le regole nella stessa VBox si combinano in AND.
  - Più VBox assegnate allo stesso utente si uniscono in OR.
  - Il filtro si applica automaticamente a inbox e ricerca.
"""

import uuid
from datetime import datetime

from sqlalchemy import (
    Boolean,
    Column,
    DateTime,
    ForeignKey,
    Index,
    String,
    Table,
    Text,
    UniqueConstraint,
    func,
)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base

# ─── Tabella di associazione VirtualBox ↔ Mailbox ─────────────────────────────
virtual_box_mailboxes = Table(
    "virtual_box_mailboxes",
    Base.metadata,
    Column(
        "virtual_box_id",
        UUID(as_uuid=True),
        ForeignKey("virtual_boxes.id", ondelete="CASCADE"),
        primary_key=True,
    ),
    Column(
        "mailbox_id",
        UUID(as_uuid=True),
        ForeignKey("mailboxes.id", ondelete="CASCADE"),
        primary_key=True,
    ),
)


class VirtualBox(Base):
    """Casella virtuale: contenitore di regole + etichetta opzionale."""

    __tablename__ = "virtual_boxes"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    tenant_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False
    )
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    label: Mapped[str | None] = mapped_column(String(100), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)

    created_by: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True), ForeignKey("users.id"), nullable=True
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
    )

    # Relazioni
    rules: Mapped[list["VirtualBoxRule"]] = relationship(
        "VirtualBoxRule", back_populates="virtual_box", cascade="all, delete-orphan",
        order_by="VirtualBoxRule.created_at"
    )
    assignments: Mapped[list["VirtualBoxAssignment"]] = relationship(
        "VirtualBoxAssignment", back_populates="virtual_box", cascade="all, delete-orphan"
    )
    mailboxes: Mapped[list["Mailbox"]] = relationship(  # noqa: F821
        "Mailbox",
        secondary=virtual_box_mailboxes,
        lazy="selectin",
    )

    __table_args__ = (
        UniqueConstraint("tenant_id", "name", name="uq_vbox_name_tenant"),
        Index("idx_vbox_tenant", "tenant_id"),
    )

    def __repr__(self) -> str:
        return f"<VirtualBox {self.name!r}>"


class VirtualBoxRule(Base):
    """
    Singola regola di filtro all'interno di una VBox.

    field    : mailbox_id | imap_folder | subject | from_address | to_address
    operator : contains | equals | starts_with | ends_with | regex
    value    : valore da confrontare

    Può anche specificare un date_from / date_to per filtrare per periodo.
    """

    __tablename__ = "virtual_box_rules"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    virtual_box_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("virtual_boxes.id", ondelete="CASCADE"),
        nullable=False,
    )
    field: Mapped[str] = mapped_column(String(50), nullable=False)
    operator: Mapped[str] = mapped_column(String(20), nullable=False, default="contains")
    value: Mapped[str] = mapped_column(Text, nullable=False)
    date_from: Mapped[str | None] = mapped_column(String(20), nullable=True)   # ISO date
    date_to: Mapped[str | None] = mapped_column(String(20), nullable=True)     # ISO date

    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )

    # Relazioni
    virtual_box: Mapped["VirtualBox"] = relationship(
        "VirtualBox", back_populates="rules"
    )

    __table_args__ = (
        Index("idx_vbox_rule_vbox", "virtual_box_id"),
    )

    def __repr__(self) -> str:
        return f"<VirtualBoxRule {self.field!r} {self.operator!r} {self.value!r}>"


class VirtualBoxAssignment(Base):
    """Assegnazione di una VBox a un utente."""

    __tablename__ = "virtual_box_assignments"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    virtual_box_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("virtual_boxes.id", ondelete="CASCADE"),
        nullable=False,
    )
    user_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False
    )
    assigned_by: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True), ForeignKey("users.id"), nullable=True
    )
    assigned_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )

    # Relazioni
    virtual_box: Mapped["VirtualBox"] = relationship(
        "VirtualBox", back_populates="assignments"
    )

    __table_args__ = (
        UniqueConstraint("virtual_box_id", "user_id", name="uq_vbox_assignment"),
        Index("idx_vbox_assign_user", "user_id"),
        Index("idx_vbox_assign_vbox", "virtual_box_id"),
    )

    def __repr__(self) -> str:
        return f"<VirtualBoxAssignment vbox={self.virtual_box_id} user={self.user_id}>"
