from __future__ import annotations

from enum import Enum
from typing import Literal

from pydantic import BaseModel, ConfigDict, Field


class StrictModel(BaseModel):
    model_config = ConfigDict(extra="forbid")


class SourceType(str, Enum):
    CODE = "code"
    DOCS = "docs"
    COMMIT = "commit"


class ChunkType(str, Enum):
    SYMBOL = "symbol"
    TEXT = "text"
    SUMMARY = "summary"


class HydrationSource(str, Enum):
    EMBEDDING_RECORD = "embedding_record"
    CODE_CHUNK = "code_chunk"
    DOC_CHUNK = "doc_chunk"
    COMMIT_ANALYSIS = "commit_analysis"


class RetrieveFilters(StrictModel):
    source_types: list[SourceType] | None = None


class RetrieveRequest(StrictModel):
    repo_id: str
    snapshot_id: str
    query_text: str
    filters: RetrieveFilters | None = None
    top_k: int = Field(default=20, ge=1, le=100)
    score_threshold: float = Field(default=0.25, ge=0.0, le=1.0)
    graph_expand: bool = False
    request_id: str | None = None


class HydratedHit(StrictModel):
    record_id: str
    source_type: SourceType
    score: float
    chunk_type: ChunkType
    text: str
    file_path: str | None = None
    doc_path: str | None = None
    section_title: str | None = None
    section_level: int | None = None
    symbol_name: str | None = None
    symbol_type: str | None = None
    language: str | None = None
    start_line: int | None = None
    end_line: int | None = None
    commit_sha: str | None = None
    graph_node_id: str | None = None
    impacted_symbols: list[str] = Field(default_factory=list)
    changed_files: list[str] = Field(default_factory=list)
    author_name: str | None = None
    author_email: str | None = None
    committer_name: str | None = None
    committer_email: str | None = None
    authored_at: str | None = None
    committed_at: str | None = None
    commit_message: str | None = None
    hydration_source: HydrationSource | None = None
    graph_expanded: bool = False


class CodeSnippet(StrictModel):
    record_id: str
    file_path: str
    symbol_name: str | None = None
    symbol_type: str | None = None
    language: str | None = None
    start_line: int | None = None
    end_line: int | None = None
    text: str
    score: float
    graph_expanded: bool = False


class DocExcerpt(StrictModel):
    record_id: str
    doc_path: str
    section_title: str | None = None
    section_level: int | None = None
    text: str
    score: float
    graph_expanded: bool = False


class RelatedCommit(StrictModel):
    record_id: str
    commit_sha: str
    summary: str
    impacted_symbols: list[str] = Field(default_factory=list)
    changed_files: list[str] = Field(default_factory=list)
    score: float
    author_name: str | None = None
    author_email: str | None = None
    committer_name: str | None = None
    committer_email: str | None = None
    authored_at: str | None = None
    committed_at: str | None = None
    commit_message: str | None = None


class RetrieveMetadata(StrictModel):
    request_id: str | None = None
    latency_ms: int | None = None
    vector_latency_ms: int | None = None
    hydrate_latency_ms: int | None = None
    graph_latency_ms: int | None = None
    retrieval_count: int | None = None
    hydrated_count: int | None = None
    skipped_count: int | None = None
    graph_nodes_expanded: int | None = None
    graph_records_added: int | None = None


class RetrieveResponse(StrictModel):
    embedding_model: str
    embedding_dimension: int
    hits: list[HydratedHit] = Field(default_factory=list)
    code_snippets: list[CodeSnippet] = Field(default_factory=list)
    doc_excerpts: list[DocExcerpt] = Field(default_factory=list)
    related_commits: list[RelatedCommit] = Field(default_factory=list)
    metadata: RetrieveMetadata = Field(default_factory=RetrieveMetadata)
