"""Справочники: источники, API-ключи, типы уведомлений, цели каналов.

Договор приёма — docs/05-ingestion-api.md. Секреты каналов (токен Telegram-бота,
SMTP-креды) — в .env; в channel_targets.config только идентификаторы целей
(chat_id, адрес, s2s-точка) — они не секреты, а конфигурация.
"""

from datetime import datetime

from sqlalchemy import (
    CheckConstraint,
    DateTime,
    ForeignKey,
    Integer,
    String,
    Text,
    UniqueConstraint,
    func,
)
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column

from app.database import Base

# Каналы доставки — те же значения в channel_targets и routing_rule_actions.
# internal_log не имеет внешних кредов (это БД); push (web-push) пока не включаем.
CHANNELS = ("telegram", "email", "s2s", "internal_log")

PRIORITIES = ("low", "normal", "high", "critical")

# Статусы конверта события (events.status):
#   queued     — принято на приёме, ждёт воркера
#   processing — воркер маршрутизирует/раскидывает доставки
#   done       — все доставки в терминальном состоянии
#   failed     — событие не маршрутизировано (после финальных ретраев правил)
EVENT_STATUSES = ("queued", "processing", "done", "failed")


class Source(Base):
    """Источник событий; регистрируется в админке, к нему выдаётся API-ключ."""

    __tablename__ = "sources"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    name: Mapped[str] = mapped_column(String(64), unique=True)  # slug: "monitoring"
    label: Mapped[str | None] = mapped_column(String(120))  # человекочитаемое
    # Описание «что это за сервис» для человека и ИИ-агента (админка Synapse):
    # не гадать по имени источника. В конверт события и s2s-вебхук не входит.
    description: Mapped[str | None] = mapped_column(Text)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )


class ApiKey(Base):
    """API-ключ источника (`Authorization: Bearer syn_...`).

    Хранится только sha256-хэш (token_hash) и последние 4 символа (token_hint)
    для поиска в UI. Ключ — удостоверение источника: событие обязано нести
    source, совпадающий с ключом, иначе 403. Ротация ключа — новая запись
    той же строкой sources, старая — revoked_at.
    """

    __tablename__ = "api_keys"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    source_id: Mapped[int] = mapped_column(
        ForeignKey("sources.id", ondelete="CASCADE"), index=True
    )
    name: Mapped[str] = mapped_column(String(120))  # человекочитаемое назначение
    token_hash: Mapped[str] = mapped_column(String(64), unique=True)
    token_hint: Mapped[str] = mapped_column(String(8))
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    last_used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))


class NotificationType(Base):
    """Тип уведомления = тройка (source, subject, action) из контракта."""

    __tablename__ = "notification_types"
    __table_args__ = (
        UniqueConstraint("source_id", "subject", "action"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    source_id: Mapped[int] = mapped_column(
        ForeignKey("sources.id", ondelete="CASCADE"), index=True
    )
    subject: Mapped[str] = mapped_column(String(64))
    action: Mapped[str] = mapped_column(String(64))
    # Опциональная JSON Schema payload'а; проверяется воркером, не на приёме.
    payload_schema: Mapped[dict | None] = mapped_column(JSONB)
    description: Mapped[str | None] = mapped_column(Text)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )


class ChannelTarget(Base):
    """Цель канала: куда именно доставлять (TG-чат, адрес, s2s-точка).

    config — JSON с идентификатором цели; формат зависит от channel.
    Примеры: telegram → {"chat_id": "@infra-squad"}, email → {"to": "ops@…"},
    s2s → {"endpoint": "http://navi-rei:…/hooks", "token_ref": "navi-rei"}.
    """

    __tablename__ = "channel_targets"
    __table_args__ = (CheckConstraint(f"channel IN {CHANNELS}", name="ck_channel"),)

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    channel: Mapped[str] = mapped_column(String(32))
    name: Mapped[str] = mapped_column(String(120))
    # Описание «что это за клиент-сервис» — для человека и ИИ-агента в админке:
    # не гадать по названию (например, «Navi rei — домашний ассистент на swarm»).
    description: Mapped[str | None] = mapped_column(Text)
    config: Mapped[dict] = mapped_column(JSONB)
    enabled: Mapped[bool] = mapped_column(default=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )