"""Telegram channel + assignment request / response schemas.

Uses repo_core base classes:
    - ``BaseRequestSchema`` — inbound (extra="forbid")
    - ``BaseResponseSchema`` — outbound (from_attributes=True)
"""

from __future__ import annotations

import uuid
from datetime import datetime
from typing import Any

from pydantic import BaseModel, Field

from repo_core.base.schemas import BaseRequestSchema, BaseResponseSchema


# ── Channel CRUD schemas ────────────────────────────────────────────────────

class ChannelCreateRequest(BaseRequestSchema):
    """Payload for manually adding a channel.

    ``channel_id`` is the **numeric** Telegram channel ID (e.g.
    ``-1001234567890``).  To add a channel by @username or invite link,
    first resolve it via the channel-preview endpoint.
    """
    channel_id: int = Field(..., description="Numeric Telegram channel ID (e.g. -1001234567890)")
    title: str = Field(..., min_length=1, max_length=512)
    username: str | None = None
    kind: str = Field(default="channel", description="channel, supergroup, group, public_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
    backfill_threshold: int = Field(default=30, description="Minimum gap size in messages to trigger backfill (gaps smaller than this are ignored)")


class ChannelUpdateRequest(BaseRequestSchema):
    """Partial update for channel metadata.  All fields optional."""
    title: str | None = None
    username: str | None = None
    kind: str | None = None
    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 | None = None
    backfill_threshold: int | None = None
    saved_preview: dict[str, Any] | None = None
    saved_preview_at: datetime | None = None


class ChannelResponse(BaseResponseSchema):
    """Channel API response."""
    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
    saved_preview: dict[str, Any] | None = None
    saved_preview_at: datetime | None = None
    deleted_at: datetime | None = None
    created_at: datetime | None = None
    updated_at: datetime | None = None


class ChannelListResponse(BaseModel):
    """Paginated list of channels."""
    items: list[ChannelResponse]
    total: int
    offset: int
    limit: int


# ── Assignment schemas ──────────────────────────────────────────────────────

class ChannelAssignRequest(BaseRequestSchema):
    """Assign a channel to an account.

    The account's ``account_type`` is derived automatically — the caller
    does not need to specify it.  The UNIQUE constraint on
    ``channel_accounts(channel_id, account_type)`` ensures only one
    account per type per channel.
    """
    account_id: uuid.UUID = Field(..., description="Account ID to assign")


class ChannelUnassignRequest(BaseRequestSchema):
    """Unassign a channel from an account.

    If ``account_id`` is omitted, all assignments for the channel are
    removed.  Pass ``account_id`` to remove only a specific account.
    """
    account_id: uuid.UUID | None = Field(None, description="Account ID to unassign; omit to clear all")


class ChannelAssignResponse(BaseModel):
    """Response for a channel assignment operation."""
    channel_id: uuid.UUID
    status: str  # "assigned", "already_assigned"
    account_id: uuid.UUID
    message: str | None = None
    telethon_status: str | None = None
    telethon_error: str | None = None


class ChannelUnassignResponse(BaseModel):
    """Response for a channel unassignment operation."""
    channel_id: uuid.UUID
    status: str  # "unassigned"


# ── Assignment info (for embedding in channel lists) ─────────────────────────

class AssignedAccountInfo(BaseModel):
    """Minimal account info shown in channel assignment lists."""
    account_id: uuid.UUID
    phone_number: str = ""
    username: str | None = None
    status: str = "unknown"
    account_type: str = "crawler"
    assigned_at: datetime | None = None


class ChannelWithAssignmentsResponse(BaseResponseSchema):
    """Channel API response enriched with assignment info."""
    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
    saved_preview: dict[str, Any] | None = None
    saved_preview_at: datetime | None = None
    deleted_at: datetime | None = None
    created_at: datetime | None = None
    updated_at: datetime | None = None
    assigned_accounts: list[AssignedAccountInfo] = Field(default_factory=list)
    assigned_count: int = 0


class ChannelWithAssignmentsListResponse(BaseModel):
    """Paginated list of channels with assignment info."""
    items: list[ChannelWithAssignmentsResponse]
    total: int
    offset: int
    limit: int


# ── Channel filter params ────────────────────────────────────────────────────

class ChannelFilterParams(BaseRequestSchema):
    """Optional filters for listing channels."""
    kind: str | None = None
    assigned: bool | None = None  # True=assigned only, False=unassigned only
    search: str | None = None
    status: str | None = None  # filter by account status in assignments


# ── Bulk upload schemas ──────────────────────────────────────────────────────

class BulkAssignRowResult(BaseModel):
    """Result for a single row in the uploaded file."""
    row: int
    channel_ref: str
    account_ref: str
    status: str  # "assigned", "already_assigned", "error", "channel_not_found", "account_not_found"
    message: str | None = None


class BulkAssignUploadResponse(BaseModel):
    """Response for a bulk CSV/Excel upload operation."""
    total_rows: int
    total_assigned: int
    total_skipped: int
    total_errors: int
    results: list[BulkAssignRowResult] = Field(default_factory=list)
