"""Pipeline error payload model.

Payload for ``{service}.failed`` and ``{service}.dead`` Kafka topics.
Validated by consumers before writing to Doris ``pipeline_errors`` table.
"""

from __future__ import annotations

import uuid
from datetime import datetime
from typing import Any

from pydantic import BaseModel, ConfigDict, Field


class PipelineErrorPayload(BaseModel):
    """Payload published when a pipeline error occurs.

    Published to ``{service}.failed`` by the service that catches the error.
    Published to ``{service}.dead`` after retries are exhausted or for
    non-retryable / fatal errors.

    All 26 fields map directly to columns in the ``pipeline_errors``
    Doris table.
    """

    model_config = ConfigDict(from_attributes=True, populate_by_name=True)

    # -- Primary key -------------------------------------------------------
    id: uuid.UUID = Field(default_factory=uuid.uuid4)

    # -- Classification ----------------------------------------------------
    error_category: str
    """One of: validation, parse, publish, persist, network, rate_limit, auth, unknown."""

    severity: str = "error"
    """warning | error | critical."""

    # -- Attribution (where did this happen?) ------------------------------
    source_service: str
    """Which service caught the error: tele-crawler | tele-parser | persistence-worker."""

    source_topic: str
    """Kafka topic the original message came from (or 'telethon' for crawler)."""

    source_partition: int | None = None
    source_offset: int | None = None

    # -- What failed -------------------------------------------------------
    target_topic: str | None = None
    """Topic being published to when the failure occurred (None for non-publish errors)."""

    target_operation: str | None = None
    """Operation that failed: insert | upsert | transform | validate | normalize."""

    # -- Original message identity -----------------------------------------
    telegram_message_id: int | None = None
    """Telegram native message ID from the original message."""

    peer_id: int | None = None
    """Flattened peer ID (channel_id, chat_id, or user_id)."""

    account_id: uuid.UUID | None = None
    """UUID of the Telegram crawler account in use."""

    proxy_id: str | None = None
    """Proxy ID string in use at time of failure."""

    # -- Error details -----------------------------------------------------
    error_message: str
    """str(exception) — human-readable error text."""

    error_type: str
    """type(exception).__name__ — Python exception class name."""

    error_traceback: str | None = None
    """Full Python traceback, truncated to last ~2000 characters."""

    # -- Input message -----------------------------------------------------
    payload_preview: str | None = None
    """First ~500 characters of the offending payload as a JSON string."""

    full_payload: dict[str, Any] | None = None
    """The complete input message dict that caused the error. NULL if truncated."""

    payload_truncated: bool = False
    """True if full_payload exceeded the size cap and was NOT stored."""

    payload_size_bytes: int | None = None
    """Original byte size of the full payload before any truncation."""

    # -- Retry state -------------------------------------------------------
    is_retryable: bool = True
    """Whether this error category is eligible for automatic retry."""

    retry_count: int = 0
    """How many times this specific error has been retried (0 = first occurrence)."""

    max_retries: int = 3
    """Maximum retry attempts before moving to .dead topic."""

    # -- Timestamps --------------------------------------------------------
    occurred_at: datetime = Field(default_factory=datetime.utcnow)
    """Wall-clock time when the error was caught by the service."""

    created_at: datetime | None = None
    """When the row was persisted to the database."""
