"""Pydantic Models für Request/Response Schemas."""
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime



# Auth (Magic Link)
class MagicLinkRequest(BaseModel):
    email: str = Field(min_length=1, max_length=254)


class MagicLinkResponse(BaseModel):
    message: str


class VerifyTokenRequest(BaseModel):
    token: str



class TokenResponse(BaseModel):
    access_token: str
    token_type: str = "bearer"
    username: str



class UserMeResponse(BaseModel):
    id: int
    username: str
    email: str = ""
    role: str = "member"
    org_name: str = ""
    org_slug: str = ""
    tenant_id: Optional[int] = None
    license_status: str = "unknown"
    license_type: str = ""
    read_only: bool = False
    read_only_reason: Optional[str] = None
    unlimited_budget: bool = False
    credits_total: Optional[int] = None
    credits_remaining: Optional[int] = None
    credits_percent_used: Optional[float] = None
    # 'monthly' = Kontingent wird zum Monatswechsel neu gefuellt, 'total' = gilt
    # fuer die ganze Lizenzlaufzeit. Steuert nur die Beschriftung im Frontend.
    credits_period: Optional[str] = None
    is_global_admin: bool = False
    output_language: str = "de"


# Incidents (Lagen)
class IncidentCreate(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    description: Optional[str] = None
    type: str = Field(default="adhoc", pattern="^(adhoc|research)$")
    refresh_mode: str = Field(default="manual", pattern="^(manual|auto)$")
    refresh_interval: int = Field(default=15, ge=10, le=10080)
    refresh_start_time: Optional[str] = Field(default=None, pattern=r"^([01]\d|2[0-3]):[0-5]\d$")
    retention_days: int = Field(default=0, ge=0, le=999)
    international_sources: bool = False
    include_telegram: bool = False
    include_x: bool = False
    visibility: str = Field(default="public", pattern="^(public|private)$")
    # KI-Weg dieser Lage (EU-Umbau). Leer/None = Vorgabe der Organisation
    # bzw. globaler Default. 'cli' = Anthropic, 'bedrock' = EU (Frankfurt + staan).
    ai_backend: Optional[str] = Field(default=None, pattern="^(cli|bedrock)$")


class IncidentUpdate(BaseModel):
    title: Optional[str] = None
    description: Optional[str] = None
    type: Optional[str] = Field(default=None, pattern="^(adhoc|research)$")
    status: Optional[str] = Field(default=None, pattern="^(active|archived)$")
    refresh_mode: Optional[str] = Field(default=None, pattern="^(manual|auto)$")
    refresh_interval: Optional[int] = Field(default=None, ge=10, le=10080)
    refresh_start_time: Optional[str] = Field(default=None, pattern=r"^([01]\d|2[0-3]):[0-5]\d$")
    retention_days: Optional[int] = Field(default=None, ge=0, le=999)
    international_sources: Optional[bool] = None
    include_telegram: Optional[bool] = None
    include_x: Optional[bool] = None
    visibility: Optional[str] = Field(default=None, pattern="^(public|private)$")
    # KI-Weg. Leerstring "" setzt bewusst auf "Vorgabe der Organisation" zurueck,
    # deshalb hier auch der Leerstring erlaubt (exclude_none im Router laesst
    # None durchfallen, "" kommt an und wird zu NULL).
    ai_backend: Optional[str] = Field(default=None, pattern="^(cli|bedrock|)$")


class DescriptionEnhanceRequest(BaseModel):
    title: str = Field(min_length=3)
    description: str | None = None
    type: str = Field(default="adhoc", pattern="^(adhoc|research)$")


class IncidentResponse(BaseModel):
    """Vollstaendige Lage-Details (fuer GET /incidents/{id}).

    Enthaelt summary + latest_developments, aber NICHT mehr sources_json —
    das wird separat per GET /incidents/{id}/sources geladen (Lazy-Load).
    """
    id: int
    title: str
    description: Optional[str]
    type: str
    status: str
    refresh_mode: str
    refresh_interval: int
    refresh_start_time: Optional[str] = None
    retention_days: int
    visibility: str = "public"
    summary: Optional[str]
    latest_developments: Optional[str] = None
    public_mood: Optional[str] = None
    public_mood_updated_at: Optional[str] = None
    international_sources: bool = True
    include_telegram: bool = False
    include_x: bool = False
    ai_backend: Optional[str] = None
    created_by: int
    created_by_username: str = ""
    created_at: str
    updated_at: str
    article_count: int = 0
    source_count: int = 0


class IncidentListItem(BaseModel):
    """Schlankes Sidebar-Item (fuer GET /incidents).

    Enthaelt, was Sidebar und Edit-Dialog brauchen — kein summary,
    kein sources_json. Statt summary-Volltext ein ``has_summary``-Bit,
    damit das Frontend "erster Refresh"-Zustand erkennen kann.
    description bleibt drin (kurz, vom Edit-Modal direkt genutzt).
    """
    id: int
    title: str
    description: Optional[str] = None
    type: str
    status: str
    refresh_mode: str
    refresh_interval: int
    refresh_start_time: Optional[str] = None
    retention_days: int
    visibility: str = "public"
    international_sources: bool = True
    include_telegram: bool = False
    include_x: bool = False
    ai_backend: Optional[str] = None
    created_by: int
    created_by_username: str = ""
    created_at: str
    updated_at: str
    article_count: int = 0
    source_count: int = 0
    has_summary: bool = False




# Sources (Quellenverwaltung)
SOURCE_TYPE_PATTERN = "^(rss_feed|web_source|excluded|telegram_channel|podcast_feed|pdf_document|x_account)$"
SOURCE_CATEGORY_PATTERN = "^(nachrichtenagentur|oeffentlich-rechtlich|qualitaetszeitung|behoerde|fachmedien|think-tank|international|regional|boulevard|sonstige|x)$"
SOURCE_STATUS_PATTERN = "^(active|inactive)$"
class SourceCreate(BaseModel):
    name: str = Field(min_length=1, max_length=200)
    url: Optional[str] = None
    domain: Optional[str] = None
    source_type: str = Field(default="rss_feed", pattern=SOURCE_TYPE_PATTERN)
    category: str = Field(default="sonstige", pattern=SOURCE_CATEGORY_PATTERN)
    status: str = Field(default="active", pattern=SOURCE_STATUS_PATTERN)
    notes: Optional[str] = None
    language: Optional[str] = None
    bias: Optional[str] = None


class SourceUpdate(BaseModel):
    name: Optional[str] = Field(default=None, max_length=200)
    url: Optional[str] = None
    domain: Optional[str] = None
    source_type: Optional[str] = Field(default=None, pattern=SOURCE_TYPE_PATTERN)
    category: Optional[str] = Field(default=None, pattern=SOURCE_CATEGORY_PATTERN)
    status: Optional[str] = Field(default=None, pattern=SOURCE_STATUS_PATTERN)
    notes: Optional[str] = None
    language: Optional[str] = None
    bias: Optional[str] = None


class SourceResponse(BaseModel):
    id: int
    name: str
    url: Optional[str]
    domain: Optional[str]
    source_type: str
    category: str
    status: str
    notes: Optional[str]
    added_by: Optional[str]
    article_count: int = 0
    last_seen_at: Optional[str] = None
    created_at: str
    language: Optional[str] = None
    bias: Optional[str] = None
    political_orientation: Optional[str] = None
    media_type: Optional[str] = None
    reliability: Optional[str] = None
    state_affiliated: bool = False
    country_code: Optional[str] = None
    classification_source: Optional[str] = None
    classified_at: Optional[str] = None
    alignments: list[str] = []
    is_global: bool = False
    ifcn_signatory: bool = False
    eu_disinfo_listed: bool = False
    eu_disinfo_case_count: int = 0
    eu_disinfo_last_seen: Optional[str] = None
    external_data_synced_at: Optional[str] = None


# Source Discovery
class DiscoverRequest(BaseModel):
    url: str = Field(min_length=1, max_length=500)


class DiscoverResponse(BaseModel):
    name: str
    domain: str
    rss_url: Optional[str] = None
    category: str
    source_type: str


# Multi-Discovery
class DiscoverMultiResponse(BaseModel):
    domain: str
    category: str
    added_count: int
    skipped_count: int
    total_found: int
    sources: list[SourceResponse]
    fallback_single: bool = False


# Domain-Aktionen (Ausschließen/Ausschluss aufheben)
class DomainActionRequest(BaseModel):
    domain: str = Field(min_length=1, max_length=200)
    notes: Optional[str] = None



# Notifications
class NotificationResponse(BaseModel):
    id: int
    incident_id: Optional[int]
    type: str
    title: str
    text: str
    icon: str
    is_read: bool
    created_at: str


class NotificationMarkReadRequest(BaseModel):
    notification_ids: Optional[list[int]] = None  # None = alle



class SubscriptionUpdate(BaseModel):
    notify_email_summary: bool = False
    notify_email_new_articles: bool = False
    notify_email_status_change: bool = False


class SubscriptionResponse(BaseModel):
    notify_email_summary: bool = False
    notify_email_new_articles: bool = False
    notify_email_status_change: bool = False



class FeedbackRequest(BaseModel):
    category: str = Field(pattern="^(bug|feature|question|other)$")
    message: str = Field(min_length=10, max_length=5000)




# --- Global Admin: Org-Wechsel (herausnehmbar) ---

class SwitchOrgRequest(BaseModel):
    organization_id: int


class OrgListItem(BaseModel):
    id: int
    name: str
    slug: str
    is_active: bool
