"""Telegram channel domain model — Pydantic v2 with ORM compatibility.

- :class:`TelegramChannel` — stores channel metadata.  The ``account_id``
  FK is **deprecated** in favour of the ``channel_accounts`` junction table.
- :class:`ChannelAccount` — many-to-many junction allowing channels to be
  assigned to multiple accounts of different types (crawler, downloader, etc.).
"""

from __future__ import annotations

import uuid
from datetime import datetime
from typing import Any

from pydantic import field_validator

from repo_core.base.models import DomainModel


class TelegramChannel(DomainModel):
    """Telegram channel entity — stored in the ``telegram_channels`` table.

    Each row represents a unique Telegram channel that has been discovered
    or explicitly added by an admin.

    Assignment state lives exclusively in the ``channel_accounts`` junction
    table — there is no direct FK from channels to accounts.
    """

    id: uuid.UUID
    channel_id: int
    title: str
    username: str | None = None
    kind: str = "channel"
    member_count: int | None = None
    profile_photo_base64: str | None = None
    description: str | None = None
    linked_chat_id: int | None = None
    admins_count: int | None = None
    online_count: int | None = None
    slowmode_seconds: int | None = None
    can_view_participants: bool | None = None
    verified: bool | None = None
    restricted: bool | None = None
    scam: bool | None = None
    fake: bool | None = None
    broadcast: bool | None = None
    megagroup: bool | None = None
    avg_views_last_10: float | None = None
    avg_reactions_last_10: float | None = None
    avg_comments_last_10: float | None = None
    total_posts_analyzed: int = 0
    backfill_threshold: int = 30
    """Minimum gap size in messages to trigger backfill.

    Gaps smaller than this count are ignored by the gap scanner.
    Default 30 means only gaps of 30+ missing messages get backfilled.
    """
    saved_preview: dict[str, Any] | None = None
    saved_preview_at: datetime | None = None
    created_at: datetime | None = None
    updated_at: datetime | None = None

    @field_validator('total_posts_analyzed', mode='before')
    @classmethod
    def _coerce_total_posts_analyzed(cls, v: object) -> int:
        """Coerce NULL (from DB) to 0 so model construction never fails."""
        if v is None:
            return 0
        return int(v)  # type: ignore[arg-type]


class ChannelAccount(DomainModel):
    """Many-to-many junction between ``telegram_channels`` and
    ``telegram_accounts`` — stored in the ``channel_accounts`` table.

    A channel can be assigned to one account per ``account_type``
    (e.g. one crawler *and* one downloader, but not two crawlers).
    The unique constraint ``(channel_id, account_type)`` enforces this.

    ``account_type`` is denormalised from ``telegram_accounts`` so the
    unique constraint can operate at the database level without a JOIN.
    """

    id: uuid.UUID
    channel_id: uuid.UUID
    """FK to ``telegram_channels.id``."""
    account_id: uuid.UUID
    """FK to ``telegram_accounts.id``."""
    account_type: str
    """Denormalised from the account row.  Values: ``crawler``, ``downloader``.
    Manager accounts (``account_type = 'manager'``) are never assigned to
    channels via this junction table.
    """
    assigned_at: datetime | None = None
    assigned_by: str | None = None
    is_active: bool = True
    created_at: datetime | None = None
    updated_at: datetime | None = None
