"""
Modelli Notifiche Multi-canale.

Struttura:
  NotificationChannel  → configurazione canale (Webhook, Email, Telegram, WhatsApp)
  NotificationRule     → regola evento → canale
  NotificationLog      → log tentativi di invio (con retry e circuit breaker)

Canali supportati:
  webhook    – POST HMAC-SHA256
  email      – SMTP
  telegram   – Bot API
  whatsapp   – Meta Cloud API v18

Sicurezza:
  config_enc – configurazione sensibile cifrata AES-256-GCM
"""

import uuid
from datetime import datetime

from sqlalchemy import (
    BigInteger,
    Boolean,
    DateTime,
    Enum,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
    func,
)
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base

ChannelType = Enum(
    "webhook", "email", "telegram", "whatsapp",
    name="notification_channel_type",
    create_type=False,
)

NotificationStatus = Enum(
    "pending", "sent", "failed", "skipped",
    name="notification_status",
    create_type=False,
)


class NotificationChannel(Base):
    """
    Canale di notifica configurato da un tenant.

    Il campo config_enc contiene la configurazione sensibile (API key,
    token, SMTP password…) cifrata AES-256-GCM a livello applicativo.
    """

    __tablename__ = "notification_channels"

    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)
    channel_type: Mapped[str] = mapped_column(ChannelType, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)

    # Configurazione NON sensibile (JSON libero: url, chat_id, from_email…)
    config: Mapped[dict | None] = mapped_column(JSONB, nullable=True)

    # Configurazione sensibile cifrata (token, password…) – base64(GCM(json))
    config_enc: Mapped[str | None] = mapped_column(Text, nullable=True)

    # Circuit breaker
    consecutive_failures: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    circuit_open_until: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=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["NotificationRule"]] = relationship(
        "NotificationRule", back_populates="channel", cascade="all, delete-orphan"
    )
    logs: Mapped[list["NotificationLog"]] = relationship(
        "NotificationLog", back_populates="channel", cascade="all, delete-orphan"
    )

    __table_args__ = (
        Index("idx_notif_channel_tenant", "tenant_id"),
    )

    def __repr__(self) -> str:
        return f"<NotificationChannel {self.name!r} type={self.channel_type!r}>"


class NotificationRule(Base):
    """
    Regola: evento PEChub → canale di notifica.

    event_type: new_message | state_changed | anomaly | send_failed | ...
    filter:     JSONB con condizioni opzionali (mailbox_id, state, ecc.)
    """

    __tablename__ = "notification_rules"

    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
    )
    channel_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("notification_channels.id", ondelete="CASCADE"),
        nullable=False,
    )
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    event_type: Mapped[str] = mapped_column(String(100), nullable=False)
    filter: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)

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

    # Relazioni
    channel: Mapped["NotificationChannel"] = relationship(
        "NotificationChannel", back_populates="rules"
    )

    __table_args__ = (
        Index("idx_notif_rule_tenant", "tenant_id"),
        Index("idx_notif_rule_channel", "channel_id"),
        Index("idx_notif_rule_event", "event_type"),
    )

    def __repr__(self) -> str:
        return f"<NotificationRule {self.name!r} event={self.event_type!r}>"


class NotificationLog(Base):
    """
    Log di ogni tentativo di notifica.

    Retry indipendente per canale: max 3 tentativi, backoff 5m → 30m → 2h.
    """

    __tablename__ = "notification_log"

    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
    )
    channel_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("notification_channels.id", ondelete="CASCADE"),
        nullable=False,
    )
    rule_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("notification_rules.id", ondelete="SET NULL"),
        nullable=True,
    )
    event_type: Mapped[str] = mapped_column(String(100), nullable=False)
    event_payload: Mapped[dict | None] = mapped_column(JSONB, nullable=True)

    status: Mapped[str] = mapped_column(NotificationStatus, nullable=False, default="pending")
    attempt_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    max_attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=3)
    next_retry_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
    http_status: Mapped[int | None] = mapped_column(Integer, nullable=True)

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

    # Relazioni
    channel: Mapped["NotificationChannel"] = relationship(
        "NotificationChannel", back_populates="logs"
    )

    __table_args__ = (
        Index("idx_notif_log_tenant", "tenant_id"),
        Index("idx_notif_log_channel", "channel_id"),
        Index("idx_notif_log_status", "status", "next_retry_at"),
    )

    def __repr__(self) -> str:
        return f"<NotificationLog channel={self.channel_id} status={self.status!r}>"
