"""
Notification delivery model.

Tables in this file:
  notification — multi-channel notification dispatch records (SMS, Email, Portal)

Notification Architecture & Lifecycle
──────────────────────────────────────
1. Triggers:
   Notifications are triggered by asynchronous events:
     - Report released → SMS/Email sent with 72-hour secure view/download link
     - Appointment confirmed / reminder → SMS reminder before appointment slot
     - Visit order created → SMS confirmation with bill receipt PIN hash

2. Multi-Channel Support:
   - SMS: Delivered via local SMS gateways (Sparrow SMS, Aakash SMS)
   - EMAIL: Transactional email with report links or receipts
   - PORTAL: In-app/link notifications

3. Delivery Lifecycle:
   PENDING -> awaiting dispatch by the notification service
   SENT    -> successfully dispatched by provider gateway (sent_at set)
   FAILED  -> gateway error or invalid recipient (error_message set)

Soft Delete Decisions
─────────────────────
  Notification -> NO (no soft delete): communications history acts as a
                  permanent audit log of messages sent to patients.

Lab Scoping
───────────
  Notification -> NO (scoped indirectly via patient_id, order_id, or report_id).
"""

import datetime
import enum
import uuid

import sqlalchemy as sa
from sqlalchemy import (
    ForeignKey,
    String,
    Text,
)
from sqlalchemy.dialects.postgresql import TIMESTAMP, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.models.base import (
    Base,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)

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


class NotificationChannel(enum.StrEnum):
    """Delivery transport channels for notifications."""

    SMS = "sms"
    EMAIL = "email"
    PORTAL = "portal"


class NotificationStatus(enum.StrEnum):
    """Dispatch status of the notification."""

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


NotificationChannelType = sa.Enum(
    NotificationChannel,
    name="notification_channel",
    native_enum=True,
    create_constraint=True,
    values_callable=lambda obj: [e.value for e in obj],
)

NotificationStatusType = sa.Enum(
    NotificationStatus,
    name="notification_status",
    native_enum=True,
    create_constraint=True,
    values_callable=lambda obj: [e.value for e in obj],
)


# ── Models ─────────────────────────────────────────────────────────────────────


class Notification(UUIDPrimaryKeyMixin, TimestampMixin, Base):
    """
    Log of outgoing notifications sent to patients.

    Tracks channel, dispatch status, recipient address/phone, message body,
    and gateway response / error details for auditing and retries.
    """

    __tablename__ = "notification"

    # ── Foreign Keys ───────────────────────────────────────────────────────────
    patient_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("patient.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        comment="FK → patient.id. Target recipient of the notification.",
    )
    order_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("visit_order.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="FK → visit_order.id. Optional order context if message is order-related.",
    )
    report_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("report.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="FK → report.id. Optional report context if message is report-release link.",
    )

    # ── Message Attributes ─────────────────────────────────────────────────────
    channel: Mapped[NotificationChannel] = mapped_column(
        NotificationChannelType,
        nullable=False,
        index=True,
        comment="Transport channel: SMS, Email, or Portal message",
    )
    status: Mapped[NotificationStatus] = mapped_column(
        NotificationStatusType,
        nullable=False,
        default=NotificationStatus.PENDING,
        server_default="pending",
        index=True,
        comment="Dispatch and delivery status",
    )
    recipient: Mapped[str] = mapped_column(
        String(255),
        nullable=False,
        comment="Destination address: phone number (E.164) or email address",
    )
    subject: Mapped[str | None] = mapped_column(
        String(255),
        nullable=True,
        comment="Subject line for email, or title for portal notification",
    )
    message: Mapped[str] = mapped_column(
        Text,
        nullable=False,
        comment="Body content of the notification message or SMS text",
    )
    sent_at: Mapped[datetime.datetime | None] = mapped_column(
        TIMESTAMP(timezone=True),
        nullable=True,
        comment="Timestamp when successfully dispatched to provider (TIMESTAMPTZ)",
    )
    error_message: Mapped[str | None] = mapped_column(
        Text,
        nullable=True,
        comment="Failure reason or gateway response error if dispatch failed",
    )

    # ── Relationships ──────────────────────────────────────────────────────────
    patient: Mapped["Patient"] = relationship(  # type: ignore[name-defined]
        "Patient",
        foreign_keys="[Notification.patient_id]",
        lazy="selectin",
    )
    order: Mapped["VisitOrder | None"] = relationship(  # type: ignore[name-defined]
        "VisitOrder",
        foreign_keys="[Notification.order_id]",
        lazy="selectin",
    )
    report: Mapped["Report | None"] = relationship(  # type: ignore[name-defined]
        "Report",
        foreign_keys="[Notification.report_id]",
        lazy="selectin",
    )

    def __repr__(self) -> str:
        return (
            f"<Notification id={self.id!s:.8} "
            f"channel={self.channel.value!r} "
            f"recipient={self.recipient!r} "
            f"status={self.status.value!r}>"
        )
