from __future__ import annotations

from typing import Any, Literal

from pydantic import BaseModel, Field, computed_field, model_validator

SearchStrategy = Literal["bm25", "vector", "hybrid", "exact"]


class EmbeddingConfig(BaseModel):
    key: str
    source_column: str
    provider: str = "default"
    model: str = "default"

    @computed_field
    @property
    def target_column(self) -> str:
        return f"ds_{self.key}"


class BM25Config(BaseModel):
    key: str
    source_column: str


class ExactConfig(BaseModel):
    key: str
    source_column: str


class TableConfig(BaseModel):
    table: str
    embeddings: list[EmbeddingConfig]
    pk_column: str = "id"
    bm25: list[BM25Config] = Field(default_factory=list)
    exact: list[ExactConfig] = Field(default_factory=list)
    batch_size: int = 500
    index_id: str = ""

    @model_validator(mode="after")
    def _validate(self) -> TableConfig:
        if not self.index_id:
            self.index_id = self.table
        all_keys = (
            [e.key for e in self.embeddings]
            + [b.key for b in self.bm25]
            + [e.key for e in self.exact]
        )
        dupes = [k for k in all_keys if all_keys.count(k) > 1]
        if dupes:
            raise ValueError(f"Duplicate keys in TableConfig: {sorted(set(dupes))}")
        return self


class SearchConfig(BaseModel):
    strategy: SearchStrategy = "hybrid"
    limit: int = 10
    where: str | None = None
    weights: dict[str, float] = Field(default_factory=dict)


class SearchResult(BaseModel):
    id: Any
    score: float
    row: dict[str, Any]
