from pydantic import BaseModel, Field
from typing import Literal

FormatType = Literal[
    "Guide",
    "FullGuide",
    "Rest",
]

from config import DEFAULT_PROVIDER

ProviderType = Literal["claude", "minimax", "lokal"]

SourceType = Literal["thema", "projekt", "uni", "link"]


class GuideCreateRequest(BaseModel):
    topic: str = Field(min_length=1, max_length=100)
    format: FormatType
    instructions: str = Field(default="", max_length=2000)
    provider: ProviderType = DEFAULT_PROVIDER


class TopicCreateRequest(BaseModel):
    name: str = Field(min_length=1, max_length=100)


class QaRunRequest(BaseModel):
    topic: str = Field(min_length=1, max_length=100)
    llm: bool = True  # wie das Gate: Echtheits-/Dubletten-Stichprobe inklusive


class RepairRequest(BaseModel):
    topic: str = Field(min_length=1, max_length=100)
    ebene: str | None = None  # None = alle Fixes, "inventory" oder "artefacts" filtert je Ebene


class BlocksCreateRequest(BaseModel):
    topic: str = Field(min_length=1, max_length=100)
    instructions: str = Field(default="", max_length=2000)
    provider: ProviderType = DEFAULT_PROVIDER
    source_type: SourceType = "thema"
    source_location: str = Field(default="", max_length=20000)  # link mode: one URL per line
    research: bool = True  # False = Continue: drain the existing kanban queue, no new search
    qa_force: bool = False  # True = übersteuert ein pausierendes QA-Gate („Trotzdem fortsetzen")
    # „Auto" pro Ebene: nach der QA im Loop „Befunde beheben", bis 100 % / Stillstand / 10×.
    # Aus = nach der QA stoppen (Kontrollpunkt). Default an.
    auto_inventory: bool = True
    auto_artefacts: bool = True
    auto_guide: bool = True


class GuideFormatRequest(BaseModel):
    topic: str = Field(min_length=1)
    format: str = Field(min_length=1)


class PracticeAnswerRequest(BaseModel):
    topic: str = Field(min_length=1)
    block_norm: str = Field(min_length=1, max_length=300)
    sub_norm: str = Field(max_length=300)
    correct: bool




class BlocksStatusResponse(BaseModel):
    ready: bool
    generating: bool
    progress: str | None = None
    error: str | None = None
    partial: bool = False


class FolderResponse(BaseModel):
    name: str
    location: str  # path relative to the repo root (e.g. "projects/foo")


class BlocksSourceUpdate(BaseModel):
    topic: str = Field(min_length=1, max_length=100)
    type: SourceType = "thema"
    location: str = Field(default="", max_length=20000)  # link mode: one URL per line
    spec: str = Field(default="", max_length=2000)


class BlocksSourceResponse(BaseModel):
    type: SourceType
    location: str
    spec: str


class SubblockInfo(BaseModel):
    title: str
    level: Literal["beginner", "advanced", "expert", "easy", "medium", "hard"]
    relevance: Literal["relevant", "peripheral"] | None = None


class BlockOverview(BaseModel):
    num: int
    title: str
    description: str = ""
    subblocks: list[SubblockInfo] = []


class ProviderInfo(BaseModel):
    id: str
    available: bool


class GuideResponse(BaseModel):
    id: str
    topic: str
    format: str
    status: str
    progress: str | None = None
    step: int | None = None
    error_msg: str | None = None
    created_at: str
    updated_at: str


class ChatMessage(BaseModel):
    role: Literal["user", "assistant"]
    content: str = Field(min_length=1, max_length=8000)


class GuideChatRequest(BaseModel):
    section: str = Field(default="", max_length=20000)
    outline: str = Field(default="", max_length=8000)
    messages: list[ChatMessage] = Field(min_length=1)
    provider: ProviderType = DEFAULT_PROVIDER


class GuideChatResponse(BaseModel):
    reply: str


# --- Block learning ---

class BlockChatRequest(BaseModel):
    topic: str = Field(min_length=1, max_length=100)
    block: str = Field(min_length=1, max_length=200)
    section: str = Field(default="", max_length=20000)         # detailed version
    section_compact: str = Field(default="", max_length=20000)  # compact version (mnemonics)
    messages: list[ChatMessage] = Field(min_length=1)
    provider: ProviderType = DEFAULT_PROVIDER


class BlockChatResponse(BaseModel):
    reply: str


class BlockExamRequest(BaseModel):
    topic: str = Field(min_length=1, max_length=100)
    block: str = Field(min_length=1, max_length=200)
    section: str = Field(default="", max_length=20000)         # detailed version
    section_compact: str = Field(default="", max_length=20000)  # compact version (mnemonics)
    action: Literal[
        "question", "discussion", "answer", "answer_check",
        "quiz_question", "quiz_answer", "gap_question", "gap_answer",
    ] = "question"
    question: str = Field(default="", max_length=2000)  # currently checked question (for discussion/answer); base anchor
    selection: list[int] = []  # quiz/gap-text choice: option indices picked by the learner
    correct: list[int] = []  # quiz/gap-text choice: correct indices (the client keeps them from generation)
    solution: str = Field(default="", max_length=500)  # gap text free: expected term
    alternatives: list[str] = []  # gap text free: accepted synonyms
    input: str = Field(default="", max_length=500)  # gap text free: typed term
    schwer: bool = False  # variant: easy (+1/−1) vs hard (+3/−1)
    last_rating: str = Field(default="", max_length=2000)  # feedback of the last rating (context for discussion)
    avoid: list[str] = []  # already-asked + earmarked questions — don't repeat them in substance
    asked_again: bool = False  # asked_again was used for this question → gain capped at +1
    reason: str = Field(default="", max_length=2000)  # "thorough check": why dissatisfied with the rating
    pattern: str = Field(default="", max_length=2000)  # drawn question pattern (seed); empty → live generation (fallback)
    # Base + cap are kept server-side (anchor / subs×25) — the client cap is only a hint.
    cap: int = Field(default=10, ge=1, le=10000)  # score cap = unlocked subblocks × 25
    messages: list[ChatMessage] = []  # dialog so far; empty = first question
    provider: ProviderType = DEFAULT_PROVIDER
    thorough: bool = False  # "thorough check": rating with a strong model (role guide)


class QuizOption(BaseModel):
    text: str
    correct: bool


class BlockExamResponse(BaseModel):
    question: str | None = None
    reply: str | None = None
    feedback: str | None = None
    points: int | None = None  # points delta of this answer (−2 … +3); fast = expected
    rating: Literal["gut", "neutral", "schlecht"] | None = None  # from the sign, for coloring
    options: list[QuizOption] | None = None  # quiz: 4 options + correct flags
    sentence: str | None = None  # gap text: sentence with a gap (___)
    solution: str | None = None  # gap text: expected term
    alternatives: list[str] | None = None  # gap text: accepted synonyms
    good_answers: int
    streak: int = 0  # current run of correct answers (per block)
    cap: int = 10  # cap_final = all subs × 25 — the frontend derives the learning level


class BlockLearnState(BaseModel):
    good_answers: int
    streak: int = 0
    cap: int = 0          # cap_final = all subblocks × 25
    cap_aktuell: int = 0  # reachable cap of the currently unlocked level
    freie_level: int = 1  # 1=A · 2=F · 3=E · 4=V


class BlockLearnStateResponse(BaseModel):
    blocks: dict[str, BlockLearnState]


# --- Block content: check + apply one section on demand (focus, right-click) ---

class BlockPruefenRequest(BaseModel):
    block: str = Field(min_length=1, max_length=200)
    spot: str = "ausführlich"      # "compact" | "ausführlich" (displayed field)
    snippet: str = Field(min_length=1, max_length=20000)  # raw markdown block
    hint: str = Field(default="", max_length=2000)   # optional addition (✏️)
    provider: ProviderType = DEFAULT_PROVIDER


class BlockPruefenResponse(BaseModel):
    revised: str   # corrected block as markdown


class BlockUebernehmenRequest(BaseModel):
    block: str = Field(min_length=1, max_length=200)
    spot: str = "ausführlich"
    alt: str = Field(min_length=1, max_length=20000)
    revised: str = Field(default="", max_length=20000)
    provider: ProviderType = DEFAULT_PROVIDER


class BlockUebernehmenResponse(BaseModel):
    compact: str
    md: str
    found: bool
