from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Protocol

from dot_search.types import SearchResult


@dataclass
class SubSearch:
    embedding_column: str
    query_embedding: list[float]
    weight: float = 1.0


@dataclass
class BM25Search:
    column: str
    query: str
    weight: float = 1.0


@dataclass
class ExactSearch:
    column: str
    query: str
    weight: float = 1.0


def validate_embedding_column(column: str) -> None:
    if not column.startswith("ds_"):
        raise ValueError(f"Embedding column must start with 'ds_', got: {column!r}")


class Store(Protocol):
    async def add_embedding_column(self, table: str, column: str, dimension: int) -> None: ...
    async def add_text_column(self, table: str, column: str) -> None: ...
    async def write_texts(self, table: str, column: str, pk_column: str, rows: list[tuple[Any, str]]) -> None: ...
    async def add_bm25_index(self, table: str, columns: list[str]) -> None: ...
    async def write_embeddings(
        self,
        table: str,
        column: str,
        pk_column: str,
        rows: list[tuple[Any, list[float]]],
    ) -> None: ...
    async def read_column(
        self,
        table: str,
        pk_column: str,
        source_column: str,
    ) -> list[tuple[Any, str]]: ...
    async def search(
        self,
        table: str,
        pk_column: str,
        subsearches: list[SubSearch],
        bm25searches: list[BM25Search],
        exact_searches: list[ExactSearch],
        where: str | None,
        limit: int,
    ) -> list[list[SearchResult]]: ...
    async def get_columns(self, table: str) -> list[str]: ...
    async def ensure_config_table(self) -> None: ...
    async def save_config(self, index_id: str, config_json: str) -> None: ...
    async def load_config(self, index_id: str) -> str | None: ...
    async def close(self) -> None: ...
