# Auto-generated by DjangoCFG - see CLAUDE.md

from __future__ import annotations
import datetime
from typing import Any

from pydantic import BaseModel, ConfigDict, Field


class CrawledPage(BaseModel):
    """
    Crawled page summary.

    Response model (includes read-only fields).
    """

    model_config = ConfigDict(
        validate_assignment=True,
        extra="allow",
        frozen=False,
    )

    url: str = ...
    success: bool = ...
    duration_ms: int | None = None



class SerperResult(BaseModel):
    """
    Single Serper organic result.

    Response model (includes read-only fields).
    """

    model_config = ConfigDict(
        validate_assignment=True,
        extra="allow",
        frozen=False,
    )

    title: str = ...
    link: str = ...
    snippet: str | None = None
    position: int = ...
    date: str | None = None



class ResearchResponse(BaseModel):
    """
    Response for full research endpoint.

    Response model (includes read-only fields).
    """

    model_config = ConfigDict(
        validate_assignment=True,
        extra="allow",
        frozen=False,
    )

    uuid: str = ...
    mode: str = ...
    status: str = ...
    query: str = ...
    search_results: list[SerperResult] = ...
    crawled_pages: list[CrawledPage] = ...
    summary: str = ...
    key_findings: list[str] = ...
    sources: list[str] = ...
    cost_usd: float = ...
    duration_ms: int | None = None
    model: str = ...
    error_message: str = ...



class ResearchRequestRequest(BaseModel):
    """
    POST /api/research/research/ — full pipeline: search + crawl + LLM.

    Request model (no read-only fields).
    """

    model_config = ConfigDict(
        validate_assignment=True,
        extra="allow",
        frozen=False,
    )

    query: str = Field(min_length=1, max_length=10000)
    num_results: int | None = Field(None, ge=1, le=100)
    crawl_top_n: int | None = Field(None, ge=0, le=20)
    model: str | None = Field(None, min_length=1, max_length=100)
    task_prompt: str | None = Field(None, max_length=5000)
    gl: str | None = Field(None, max_length=2)
    hl: str | None = Field(None, max_length=5)
    allowed_domains: list[str] | None = None
    blocked_domains: list[str] | None = None



class AsyncResearchResponse(BaseModel):
    """
    Response for async job submission (202 Accepted).

    Response model (includes read-only fields).
    """

    model_config = ConfigDict(
        validate_assignment=True,
        extra="allow",
        frozen=False,
    )

    status: str = ...
    job_id: str | None = None
    request_uuid: str = ...
    message: str = ...
    status_url: str = ...
    result: dict[str, Any] | None = None
    error: str | None = None



class JobStatus(BaseModel):
    """
    Job status response for polling.

    Response model (includes read-only fields).
    """

    model_config = ConfigDict(
        validate_assignment=True,
        extra="allow",
        frozen=False,
    )

    uuid: str = ...
    status: str = ...
    job_id: str | None = None
    queued_at: datetime.datetime | None = None
    started_at: datetime.datetime | None = None
    completed_at: datetime.datetime | None = None
    duration_ms: int | None = None
    error: str | None = None



class ResearchRequestList(BaseModel):
    """
    Serializer for listing research requests.

    Response model (includes read-only fields).
    """

    model_config = ConfigDict(
        validate_assignment=True,
        extra="allow",
        frozen=False,
    )

    uuid: str = ...
    mode: str = ...
    status: str = ...
    query: str = ...
    model: str = ...
    cost_usd: float = ...
    duration_ms: int | None = None
    created_at: datetime.datetime = ...



class SearchRequestRequest(BaseModel):
    """
    POST /api/research/search/ — Serper search only.

    Request model (no read-only fields).
    """

    model_config = ConfigDict(
        validate_assignment=True,
        extra="allow",
        frozen=False,
    )

    query: str = Field(min_length=1, max_length=10000)
    num_results: int | None = Field(None, ge=1, le=100)
    gl: str | None = Field(None, max_length=2)
    hl: str | None = Field(None, max_length=5)
    allowed_domains: list[str] | None = None
    blocked_domains: list[str] | None = None



