"""Tenant-aware persistence for the messaging service."""

from __future__ import annotations

import uuid
from datetime import datetime

from sqlalchemy import JSON, Boolean, DateTime, ForeignKey, Index, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column

from config.db import Base


def _uuid() -> str:
    return str(uuid.uuid4())


class MessagingProvider(Base):
    __tablename__ = "messaging_providers"
    __table_args__ = (
        UniqueConstraint("tenant_id", "name", name="uq_messaging_provider_tenant_name"),
        Index("ix_messaging_provider_selection", "tenant_id", "channel", "enabled", "priority"),
    )

    provider_id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
    tenant_id: Mapped[str] = mapped_column(String(64), ForeignKey("tenants.tenant_id"), nullable=False, index=True)
    name: Mapped[str] = mapped_column(String(128), nullable=False)
    driver: Mapped[str] = mapped_column(String(64), nullable=False)
    channel: Mapped[str] = mapped_column(String(16), nullable=False)
    enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    priority: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
    health_status: Mapped[str] = mapped_column(String(32), nullable=False, default="active")
    config_json: Mapped[dict] = mapped_column("config", JSON, nullable=False, default=dict)
    secret_ref: Mapped[str | None] = mapped_column(String(128), nullable=True)
    daily_limit: Mapped[int | None] = mapped_column(Integer, nullable=True)
    per_minute_limit: Mapped[int | None] = mapped_column(Integer, nullable=True)
    max_retries: Mapped[int] = mapped_column(Integer, nullable=False, default=2)
    last_health_check_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
    last_success_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
    last_error: Mapped[str | None] = mapped_column(String(512), nullable=True)
    consecutive_failures: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)


class MessagingTemplate(Base):
    __tablename__ = "messaging_templates"
    __table_args__ = (
        UniqueConstraint("tenant_id", "template_key", "channel", name="uq_messaging_template_scope"),
    )

    template_id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
    tenant_id: Mapped[str | None] = mapped_column(String(64), ForeignKey("tenants.tenant_id"), nullable=True, index=True)
    template_key: Mapped[str] = mapped_column(String(128), nullable=False, index=True)
    channel: Mapped[str] = mapped_column(String(16), nullable=False)
    version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    subject: Mapped[str | None] = mapped_column(String(255), nullable=True)
    text_body: Mapped[str] = mapped_column(Text, nullable=False)
    html_body: Mapped[str | None] = mapped_column(Text, nullable=True)
    allowed_variables_json: Mapped[list] = mapped_column("allowed_variables", JSON, nullable=False, default=list)
    enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)


class MessagingPermission(Base):
    __tablename__ = "messaging_app_permissions"
    __table_args__ = (
        UniqueConstraint("tenant_id", "requesting_app", name="uq_messaging_permission_app"),
    )

    permission_id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
    tenant_id: Mapped[str] = mapped_column(String(64), ForeignKey("tenants.tenant_id"), nullable=False, index=True)
    requesting_app: Mapped[str] = mapped_column(String(128), nullable=False)
    enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    channels_json: Mapped[list] = mapped_column("channels", JSON, nullable=False, default=list)
    templates_json: Mapped[list] = mapped_column("templates", JSON, nullable=False, default=list)
    purposes_json: Mapped[list] = mapped_column("purposes", JSON, nullable=False, default=list)
    allow_fallback: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)


class MessagingDelivery(Base):
    __tablename__ = "messaging_deliveries"
    __table_args__ = (
        UniqueConstraint("tenant_id", "requesting_app", "operation", "idempotency_key", name="uq_messaging_delivery_idempotency"),
        Index("ix_messaging_delivery_status_created", "tenant_id", "status", "created_at"),
        Index("ix_messaging_delivery_correlation", "tenant_id", "correlation_id"),
    )

    delivery_id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
    tenant_id: Mapped[str] = mapped_column(String(64), ForeignKey("tenants.tenant_id"), nullable=False, index=True)
    requesting_app: Mapped[str] = mapped_column(String(128), nullable=False)
    requested_by: Mapped[str | None] = mapped_column(String(128), nullable=True)
    operation: Mapped[str] = mapped_column(String(64), nullable=False)
    idempotency_key: Mapped[str] = mapped_column(String(128), nullable=False)
    channel: Mapped[str] = mapped_column(String(16), nullable=False)
    recipient_normalized: Mapped[str] = mapped_column(String(320), nullable=False)
    recipient_masked: Mapped[str] = mapped_column(String(320), nullable=False)
    template_key: Mapped[str] = mapped_column(String(128), nullable=False, index=True)
    template_version: Mapped[int] = mapped_column(Integer, nullable=False)
    purpose: Mapped[str] = mapped_column(String(64), nullable=False)
    provider_id: Mapped[str | None] = mapped_column(String(36), ForeignKey("messaging_providers.provider_id"), nullable=True, index=True)
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="pending")
    correlation_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
    metadata_json: Mapped[dict] = mapped_column("metadata", JSON, nullable=False, default=dict)
    error_category: Mapped[str | None] = mapped_column(String(64), nullable=True)
    provider_code: Mapped[str | None] = mapped_column(String(64), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)


class MessagingDeliveryAttempt(Base):
    __tablename__ = "messaging_delivery_attempts"
    __table_args__ = (UniqueConstraint("delivery_id", "attempt_number", name="uq_messaging_attempt_number"),)

    attempt_id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
    delivery_id: Mapped[str] = mapped_column(String(36), ForeignKey("messaging_deliveries.delivery_id"), nullable=False, index=True)
    provider_id: Mapped[str] = mapped_column(String(36), ForeignKey("messaging_providers.provider_id"), nullable=False)
    attempt_number: Mapped[int] = mapped_column(Integer, nullable=False)
    status: Mapped[str] = mapped_column(String(32), nullable=False)
    error_category: Mapped[str | None] = mapped_column(String(64), nullable=True)
    provider_code: Mapped[str | None] = mapped_column(String(64), nullable=True)
    retryable: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    latency_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
    started_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
    completed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
    next_retry_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
