from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, Optional


Payload = Dict[str, Any]


@dataclass(frozen=True)
class IndexDocument:
    id: str
    text: str
    payload: Payload = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not self.id.strip():
            raise ValueError("document id is required")
        if not self.text.strip():
            raise ValueError("document text is required")


@dataclass(frozen=True)
class SearchQuery:
    text: str
    source: Optional[str] = None
    project_id: Optional[int] = None
    project_identifier: Optional[str] = None
    doc_type: Optional[str] = None
    issue_id: Optional[int] = None
    contact_id: Optional[int] = None
    contact_email: Optional[str] = None
    date_from: Optional[str] = None
    date_to: Optional[str] = None
    limit: int = 10
    include_snippets: bool = True

    def __post_init__(self) -> None:
        if not self.text.strip():
            raise ValueError("search text is required")
        if self.limit < 1 or self.limit > 100:
            raise ValueError("limit must be between 1 and 100")


@dataclass(frozen=True)
class SearchResult:
    id: str
    score: float
    text: str
    payload: Payload

    @property
    def snippet(self) -> str:
        return self.text[:500]

    @property
    def citation(self) -> Payload:
        return {
            "id": self.id,
            "source": self.payload.get("source"),
            "doc_type": self.payload.get("doc_type"),
            "issue_id": self.payload.get("issue_id"),
            "project_identifier": self.payload.get("project_identifier"),
            "contact_id": self.payload.get("contact_id"),
            "contact_name": self.payload.get("contact_name"),
            "contact_email": self.payload.get("contact_email"),
            "url": self.payload.get("redmine_url"),
            "record_id": self.payload.get("source_record_id"),
        }

    def to_dict(self, include_snippet: bool = True) -> Payload:
        data: Payload = {
            "id": self.id,
            "score": self.score,
            "payload": self.payload,
            "citation": self.citation,
        }
        if include_snippet:
            data["snippet"] = self.snippet
        return data


def search_response(query: SearchQuery, results: list[SearchResult]) -> Payload:
    filters = {
        "source": query.source,
        "project_id": query.project_id,
        "project_identifier": query.project_identifier,
        "doc_type": query.doc_type,
        "issue_id": query.issue_id,
        "contact_id": query.contact_id,
        "contact_email": query.contact_email,
        "date_from": query.date_from,
        "date_to": query.date_to,
        "limit": query.limit,
    }
    return {
        "query": query.text,
        "filters": {key: value for key, value in filters.items() if value is not None},
        "results": [result.to_dict(include_snippet=query.include_snippets) for result in results],
    }
