"""Payload models for Telegram Kafka topics."""

from __future__ import annotations

from typing import Any

from pydantic import BaseModel, ConfigDict


class TelegramMessagePayload(BaseModel):
    """Expected payload shape for Telegram message topics.

    Mirrors the fields defined on ``repo_core``'s :class:`TelegramMessage`
    domain model.  All fields are optional — Kafka producers may send
    partial payloads, and consumers should tolerate missing keys.

    ``extra="allow"`` ensures unknown fields pass through without
    validation errors, keeping the wire format forward-compatible.
    """

    model_config = ConfigDict(extra="allow")

    # -- Core identifiers --------------------------------------------------
    message_id: int | None = None
    peer_id: int | None = None
    message_sent_at: str | None = None
    message: str | None = None
    crawled_at: str | None = None       # ISO 8601 — set by crawler

    # -- Boolean flags (default False in domain model) ---------------------
    out: bool | None = None
    mentioned: bool | None = None
    media_unread: bool | None = None
    silent: bool | None = None
    post: bool | None = None
    from_scheduled: bool | None = None
    legacy: bool | None = None
    edit_hide: bool | None = None
    pinned: bool | None = None
    noforwards: bool | None = None
    invert_media: bool | None = None
    offline: bool | None = None
    paid_suggested_post_stars: bool | None = None
    paid_suggested_post_ton: bool | None = None

    # -- Optional data fields ----------------------------------------------
    from_id: dict[str, Any] | None = None
    from_boosts_applied: int | None = None
    saved_peer_id: dict[str, Any] | None = None
    fwd_from: dict[str, Any] | None = None
    via_bot_id: int | None = None
    via_business_bot_id: int | None = None
    reply_to: dict[str, Any] | None = None
    media: dict[str, Any] | None = None
    reply_markup: dict[str, Any] | None = None
    details: list[dict[str, Any]] | None = None
    views: int | None = None
    forwards: int | None = None
    replies: dict[str, Any] | None = None
    edit_date: str | None = None  # ISO 8601 timestamp
    post_author: str | None = None
    grouped_id: int | None = None
    reactions: dict[str, Any] | None = None
    restriction_reason: list[dict[str, Any]] | None = None
    ttl_period: int | None = None
    quick_reply_shortcut_id: int | None = None
    effect: dict[str, Any] | None = None
    factcheck: dict[str, Any] | None = None
    report_delivery_until_date: str | None = None
    paid_message_stars: int | None = None
    suggested_post: dict[str, Any] | None = None

    @classmethod
    def field_names(cls) -> frozenset[str]:
        """Return the set of known field names for pass-through filtering.

        Consumers can use this to strip unknown keys from raw payloads::

            data = {k: v for k, v in raw.items() if k in model.field_names()}
        """
        return frozenset(cls.model_fields.keys())


# =========================================================================
# Parsed message payload models
# =========================================================================


class TelegramParsedMessagePayload(BaseModel):
    """Payload for a single parsed-message row (``telegram_parsed_messages`` table).

    Mirrors the :class:`repo_core.domains.telegram.telegram_parsed_message.model.TelegramParsedMessage`
    domain model.  All fields are optional — producers may send partial
    payloads, and consumers should tolerate missing keys.
    """

    model_config = ConfigDict(extra="allow")

    peer_id: int | None = None
    message_id: int | None = None
    partition_date: str | None = None  # ISO date  "2026-06-15"
    message_sent_at: str | None = None  # ISO 8601
    crawled_at: str | None = None       # ISO 8601 — from crawler
    from_id: int | None = None
    from_type: int | None = None       # 1=User, 2=Chat, 3=Channel
    message_text: str | None = None
    message_lang: str | None = None
    views: int = 0
    forwards_count: int = 0
    reply_count: int = 0
    has_media: bool = False
    media_type: str | None = None
    is_forward: bool = False
    fwd_from_peer_id: int | None = None
    fwd_from_msg_id: int | None = None
    fwd_from_date: str | None = None   # ISO 8601 timestamp
    reply_to_msg_id: int | None = None
    edit_date: str | None = None       # ISO 8601 timestamp

    # DB-only fields (PostgreSQL columns) — raw JSONB blobs + extra flags
    details: list[dict[str, Any]] | None = None
    media: dict[str, Any] | None = None
    reactions: dict[str, Any] | None = None   # raw blob (not flattened list)
    replies: dict[str, Any] | None = None
    reply_to: dict[str, Any] | None = None
    fwd_from: dict[str, Any] | None = None
    forwards: int | None = None
    is_post: bool = False
    is_pinned: bool = False


class MessageReactionPayload(BaseModel):
    """Payload for a single message-reaction row (``message_reactions`` table).

    Mirrors the :class:`repo_core.domains.telegram.telegram_parsed_message.model.MessageReaction`
    domain model.
    """

    model_config = ConfigDict(extra="allow")

    peer_id: int | None = None
    message_id: int | None = None
    emoticon: str = ""
    reaction_count: int = 0
    is_custom: bool = False


class MessageDetailPayload(BaseModel):
    """Payload for a single message-detail row (``telegram_message_details`` table).

    Mirrors the :class:`repo_core.domains.telegram.telegram_parsed_message.model.MessageDetail`
    domain model.
    """

    model_config = ConfigDict(extra="allow")

    peer_id: int | None = None
    message_id: int | None = None
    detail_type: str = ""
    detail_value: str | None = None
    offset: int = 0
    length: int = 0


class TelegramParsedPayload(BaseModel):
    """Expected payload shape for the ``telegram_message_parse_ready`` Kafka topic.

    Wraps the three model-aligned sections produced by the tele-parser
    normalizer: the parsed message, its reactions, and its entities.

    Validated as a single unit; the persistence layer routes each section
    to the appropriate Doris table.
    """

    model_config = ConfigDict(extra="allow")

    message: TelegramParsedMessagePayload | None = None
    reactions: list[MessageReactionPayload] = []
    details: list[MessageDetailPayload] = []


# =========================================================================
# Analyze payload
# =========================================================================


class TelegramAnalyzeReadyPayload(BaseModel):
    """Payload for the ``telegram_message_analyze_ready`` Kafka topic.

    Published by the tele-analyzer after detecting the message language
    and (optionally) translating the text.  The persistence layer maps
    ``detected_language`` → ``message_lang`` and ``translated_text`` →
    ``translated_text`` on existing ``telegram_parsed_messages`` rows.

    Fields:
        message_id: Telegram message ID (unique per peer).
        peer_id: Telegram peer (channel/chat/user) ID.
        detected_language: Language code detected by the analyzer
            (e.g. ``"en"``, ``"fa"``, ``"ar"``).
        translated_text: Optional machine-translated version of the
            original message text.
    """

    model_config = ConfigDict(extra="allow")

    message_id: int
    peer_id: int
    detected_language: str
    translated_text: str | None = None
