"""
models/schemas.py — All Pydantic request/response models
"""
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
from pydantic import BaseModel, Field


# ── Auth ─────────────────────────────────────────────────────────────────────

class LoginRequest(BaseModel):
    access_key: str = Field(..., min_length=4, description="Candidate access key")
    captcha_token: Optional[str] = None   # AI-verified captcha token

class LoginResponse(BaseModel):
    token: str
    token_type: str = "bearer"
    candidate: "CandidateOut"
    redirect_url: str            # derived from current_stage
    stage_name: str

class CandidateOut(BaseModel):
    id: str
    name: str
    email: str
    phone: str
    skill_set: list[str]
    position_applied: str
    last_login: Optional[str]
    last_question_attempt: Optional[str]
    current_stage: int
    access_key: str

class TokenPayload(BaseModel):
    sub: str         # candidate id
    name: str
    stage: int
    exp: int


# ── CAPTCHA ───────────────────────────────────────────────────────────────────

class CaptchaVerifyRequest(BaseModel):
    session_id: str
    selected_indices: list[int]

class CaptchaResponse(BaseModel):
    session_id: str
    challenge_type: str
    question: str
    items: list[dict]   # [{emoji, label}]

class CaptchaVerifyResponse(BaseModel):
    verified: bool
    token: Optional[str] = None   # short-lived JWT token proving captcha passed
    message: str


# ── Interview Setup ───────────────────────────────────────────────────────────

class PhotoUploadResponse(BaseModel):
    success: bool
    photo_id: str
    message: str
    fraud_check: Optional[dict] = None

class SetupCompleteRequest(BaseModel):
    photo_id: str
    camera_ok: bool
    mic_ok: bool
    network_ok: bool


# ── Coding Assessment ─────────────────────────────────────────────────────────

class RunCodeRequest(BaseModel):
    question_id: int
    language: str
    code: str

class RunCodeResponse(BaseModel):
    output: str
    status: Literal["success", "error", "warning"]
    tokens_used: Optional[int] = None

class SubmitCodeRequest(BaseModel):
    question_id: int
    question_text: str
    language: str
    code: str

class SubmitCodeResponse(BaseModel):
    submission_id: str
    question_id: int
    ai_result: Literal["correct", "incorrect", "partial"]
    ai_feedback: str
    ai_score: float          # 0.0 – 1.0
    ai_marks: int
    max_marks: int
    submitted_at: str

class FinalSubmitRequest(BaseModel):
    submissions: list[dict]  # list of {question_id, language, code, submitted_at}
    time_used_seconds: int

class FinalSubmitResponse(BaseModel):
    reference: str
    total_submitted: int
    estimated_score: int
    max_score: int
    submitted_at: str


# ── Fraud Detection ───────────────────────────────────────────────────────────

class FraudEventRequest(BaseModel):
    event_type: Literal[
        "TAB_SWITCH", "PASTE", "COPY", "NO_CAMERA",
        "CAMERA_LOST", "NO_MIC", "MULTIPLE_FACES",
        "INACTIVITY", "WINDOW_BLUR", "SCREEN_CAPTURE"
    ]
    message: str
    timestamp: str
    round: int = 1
    metadata: Optional[dict] = None

class FraudEventResponse(BaseModel):
    logged: bool
    violation_count: int
    warning_level: Literal["ok", "warning", "critical"]
    action: Literal["none", "warn", "disqualify"]
    message: str


# ── Generic ───────────────────────────────────────────────────────────────────

class SuccessResponse(BaseModel):
    success: bool = True
    message: str = "OK"
    data: Any = None

class ErrorResponse(BaseModel):
    success: bool = False
    message: str
    detail: Optional[str] = None

# ── Scenario ──────────────────────────────────────────────────────────────────

class ScenarioBase(BaseModel):
    title: str = Field(..., max_length=80)
    description: str = Field(..., max_length=300)
    category: str
    cat_label: str = ""
    context: str = Field(..., description="Rich AI context paragraph")
    learner_role: str = Field(..., max_length=60)
    learner_emoji: str = "🧑‍💼"
    ai_character: str = Field(..., max_length=60)
    ai_emoji: str = "🤖"
    ai_personality: str = ""
    difficulty: str = "Intermediate"
    turns: int = Field(8, ge=4, le=25)
    scoring: str = "Guided with Feedback"
    time_limit: str = "15 min"
    language_code: str = "en"
    language_name: str = "English"
    language_flag: str = "🇬🇧"
    industry: str = ""
    assign_group: str = "All users"
    voice_on: bool = True
    chat_on: bool = True
    report_on: bool = True
    featured: bool = False

class ScenarioCreate(ScenarioBase):
    pass

class ScenarioUpdate(ScenarioBase):
    pass

class Scenario(ScenarioBase):
    id: str
    status: str = "draft"          # draft | live
    completions: int = 0
    avg_score: float = 0.0
    created_at: str = ""
    updated_at: str = ""

    class Config:
        from_attributes = True


# ── Session / WebSocket messages ──────────────────────────────────────────────

class ChatMessage(BaseModel):
    role: str          # "user" | "assistant"
    content: str
    timestamp: Optional[str] = None

class SessionState(BaseModel):
    session_id: str
    scenario_id: str
    turn: int = 0
    messages: List[ChatMessage] = []
    started_at: str = ""
    ended_at: Optional[str] = None
    language_code: str = "en"
    # Candidate's display name, plumbed in from /sessions/start-external
    # so the WebSocket opener can greet them by name as a quick mic-check
    # round before the scripted interview begins.
    candidate_name: Optional[str] = None
    # True after the candidate has confirmed they're ready ("yes / sure /
    # ok / ready / let's go"). Until this flips, the AI stays in "Hey
    # <name>, shall we start?" greeting mode rather than burning interview
    # turns on accidental empty audio.
    greeting_done: bool = False
    # When the candidate picks an avatar in the Custom-Video picker
    # modal (only shown when IS_VIDEO_ROLEPLAY_SELECTION=true), that
    # filename is stored here and read by routes/websocket.py's
    # `_ensure_avatar()` so video mode uses the chosen face instead
    # of the seeded-random pick. None → random behaviour.
    selected_avatar: Optional[str] = None

# WebSocket message envelopes (JSON over WS)
class WSIncoming(BaseModel):
    type: str          # "text" | "audio_chunk" | "end_audio" | "end_session"
    content: Optional[str] = None      # for type=text
    audio_b64: Optional[str] = None    # for type=audio_chunk (base64 PCM16)
    meta: Dict[str, Any] = {}

class WSOutgoing(BaseModel):
    type: str          # "text_delta" | "text_done" | "audio_b64" | "turn_end" | "session_end" | "error"
    content: Optional[str] = None
    audio_b64: Optional[str] = None
    turn: Optional[int] = None
    sentiment: Optional[Dict[str, Any]] = None
    meta: Dict[str, Any] = {}


# ── Report ────────────────────────────────────────────────────────────────────

class DimensionScore(BaseModel):
    name: str
    score: float
    level: str          # high | mid | low

class FeedbackItem(BaseModel):
    type: str           # strength | weakness | suggestion
    text: str

class SessionReport(BaseModel):
    session_id: str
    scenario_title: str
    learner_role: str
    ai_character: str
    difficulty: str
    language: str
    mode: str           # voice | chat
    turns_completed: int
    duration_seconds: int
    overall_score: float
    verdict: str        # pass | needs_improvement
    dimensions: List[DimensionScore]
    feedback: List[FeedbackItem]
    sentiment_timeline: List[float]
    transcript: List[ChatMessage]
    generated_at: str

