"""Notification models for outbox-based delivery to Teams channels."""

import enum
from datetime import datetime
from typing import Optional

from sqlalchemy import (
    Boolean,
    DateTime,
    ForeignKeyConstraint,
    Index,
    Integer,
    String,
    Text,
    UniqueConstraint,
    func,
)
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column, relationship

from prism_models.base import Base, BaseModel, TimestampMixin


# ─────────────────────────────────────────────────────────────────────────────
# Enums
# ─────────────────────────────────────────────────────────────────────────────


class NotificationStatus(str, enum.Enum):
    """Lifecycle states for outbox notifications."""

    PENDING = "pending"
    SENDING = "sending"
    SENT = "sent"
    FAILED = "failed"


class NotificationSeverity(str, enum.Enum):
    """Severity levels for notification prioritization and formatting."""

    INFO = "info"
    WARN = "warn"
    ERROR = "error"
    CRITICAL = "critical"


class NotificationProvider(str, enum.Enum):
    """Supported notification providers (only Teams for now)."""

    TEAMS = "teams"

class NotificationTarget(BaseModel):
    """
    Webhook destinations for notifications.

    Each target represents a Teams channel webhook.
    Multiple event types can route to the same target.
    """

    __tablename__ = "notification_target"

    provider: Mapped[str] = mapped_column(String(50), nullable=False)
    target_key: Mapped[str] = mapped_column(String(100), nullable=False)
    display_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    webhook_url: Mapped[str] = mapped_column(Text, nullable=False)
    is_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    outbox_entries: Mapped[list["NotificationOutbox"]] = relationship(
        back_populates="target",
        foreign_keys="[NotificationOutbox.provider, NotificationOutbox.target_key]",
    )

    __table_args__ = (
        UniqueConstraint("provider", "target_key", name="uq_notification_target_provider_key"),
    )

    def __repr__(self):
        return f"<NotificationTarget(id={self.id}, provider='{self.provider}', key='{self.target_key}')>"


class NotificationOutbox(BaseModel):
    """
    Transactional outbox for reliable notification delivery.

    Design notes:
    - dedupe_key: Prevents duplicate sends at insert time
    - entity_type + entity_id: Polymorphic reference to source entity
    - locked_by + locked_at: Enables safe multi-worker polling
    - priority: Critical alerts jump ahead of digests
    - max_attempts: Per-notification retry limit
    - expires_at: Auto-expire stale notifications
    """

    __tablename__ = "notification_outbox"

    provider: Mapped[str] = mapped_column(String(50), nullable=False)
    target_key: Mapped[str] = mapped_column(String(100), nullable=False)
    event_type: Mapped[str] = mapped_column(String(100), nullable=False)
    severity: Mapped[str | None] = mapped_column(String(20), nullable=True)

    dedupe_key: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
    correlation_id: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)

    entity_type: Mapped[str | None] = mapped_column(String(50), nullable=True)
    entity_id: Mapped[str | None] = mapped_column(String(100), nullable=True)

    payload: Mapped[dict] = mapped_column(JSONB, nullable=False)

    status: Mapped[str] = mapped_column(
        String(20), default=NotificationStatus.PENDING.value, nullable=False, index=True
    )
    priority: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    attempts: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    max_attempts: Mapped[int] = mapped_column(Integer, default=5, nullable=False)
    next_attempt_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )
    last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
    sent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)

    locked_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
    locked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    target: Mapped[Optional["NotificationTarget"]] = relationship(
        back_populates="outbox_entries",
        foreign_keys=[provider, target_key],
    )

    __table_args__ = (
        ForeignKeyConstraint(
            ["provider", "target_key"],
            ["notification_target.provider", "notification_target.target_key"],
            name="fk_notification_outbox_target",
        ),
        Index("ix_notification_outbox_ready", "status", "priority", "next_attempt_at"),
        Index("ix_notification_outbox_entity", "entity_type", "entity_id"),
    )

    def __repr__(self):
        return f"<NotificationOutbox(id={self.id}, event='{self.event_type}', status='{self.status}')>"
