from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Literal


RunnerKind = Literal["runtime", "code_qa_eval"]
ModeKind = Literal["router_only", "router_rag", "full_chain"]


@dataclass(slots=True, frozen=True)
class CaseInput:
    repo_path: Path | None = None
    project_id: str | None = None
    rag_session_id: str | None = None


@dataclass(slots=True, frozen=True)
class RouterExpectation:
    intent: str | None = None
    sub_intent: str | None = None
    graph_id: str | None = None
    conversation_mode: str | None = None


@dataclass(slots=True, frozen=True)
class RetrievalExpectation:
    non_empty: bool | None = None
    min_rows: int | None = None
    direct_symbol_test_hits_max: int | None = None
    path_scope_contains: tuple[str, ...] = ()
    symbol_candidates_contain: tuple[str, ...] = ()
    layers_include: tuple[str, ...] = ()


@dataclass(slots=True, frozen=True)
class LlmExpectation:
    non_empty: bool | None = None
    contains_all: tuple[str, ...] = ()
    excludes: tuple[str, ...] = ()


@dataclass(slots=True, frozen=True)
class PipelineExpectation:
    answer_mode: str | None = None


@dataclass(slots=True, frozen=True)
class CaseExpectations:
    router: RouterExpectation = RouterExpectation()
    retrieval: RetrievalExpectation = RetrievalExpectation()
    llm: LlmExpectation = LlmExpectation()
    pipeline: PipelineExpectation = PipelineExpectation()


@dataclass(slots=True, frozen=True)
class V2Case:
    case_id: str
    runner: RunnerKind
    mode: ModeKind
    query: str
    source_file: Path
    input: CaseInput = CaseInput()
    expectations: CaseExpectations = CaseExpectations()
    notes: str = ""
    tags: tuple[str, ...] = ()


@dataclass(slots=True, frozen=True)
class ExecutionPayload:
    actual: dict
    details: dict


@dataclass(slots=True)
class V2CaseResult:
    case: V2Case
    actual: dict
    details: dict
    passed: bool
    mismatches: list[str] = field(default_factory=list)
