"""Meilisearch backend implementation."""

import meilisearch
from meilisearch.index import Index as MeiliIndex

from .base import BaseBackend, SearchHit, SearchResult
from . import register_backend

# Default index settings for Meilisearch
MEILISEARCH_INDEX_SETTINGS = {
    'searchableAttributes': ['content', 'title', 'tags'],
    'filterableAttributes': ['collection', 'category', 'tags', 'source_path'],
    'sortableAttributes': ['modified_at', 'created_at'],
    'embedders': {
        'gemma': {
            'source': 'userProvided',
            'dimensions': 768,
        }
    },
}


@register_backend('meilisearch')
class MeilisearchBackend(BaseBackend):
    """Meilisearch backend implementation."""

    def __init__(self, url: str, api_key: str = '', index_name: str = 'mykb'):
        self.url = url
        self.api_key = api_key
        self.index_name = index_name
        self.client = meilisearch.Client(url, api_key or None)

    def _get_index(self) -> MeiliIndex:
        return self.client.index(self.index_name)

    def initialize(self, collection: str, dimension: int) -> None:
        """Create and configure the mykb index if it doesn't exist."""
        # Create index (idempotent via task)
        task = self.client.create_index(self.index_name, {'primaryKey': 'id'})
        self.client.wait_for_task(task.task_uid)

        index = self._get_index()

        # Update embedder dimension if different
        settings = dict(MEILISEARCH_INDEX_SETTINGS)
        settings['embedders'] = {
            'gemma': {
                'source': 'userProvided',
                'dimensions': dimension,
            }
        }

        # Apply settings
        task = index.update_settings(settings)
        self.client.wait_for_task(task.task_uid)

    def add_documents(self, collection: str, documents: list[dict]) -> int:
        """Add or update documents."""
        if not documents:
            return 0
        index = self._get_index()
        task_info = index.add_documents(documents, primary_key='id')
        self.client.wait_for_task(task_info.task_uid)
        return len(documents)

    def delete_documents(self, collection: str, ids: list[str]) -> int:
        """Delete documents by ID."""
        if not ids:
            return 0
        index = self._get_index()
        task_info = index.delete_documents(ids)
        self.client.wait_for_task(task_info.task_uid)
        return len(ids)

    def update_embeddings(self, collection: str, id_vectors: dict[str, list[float]]) -> int:
        """Update document embeddings."""
        if not id_vectors:
            return 0
        index = self._get_index()
        updates = [{'id': doc_id, '_vectors': {'gemma': vec}} for doc_id, vec in id_vectors.items()]
        task_info = index.update_documents(updates)
        self.client.wait_for_task(task_info.task_uid)
        return len(updates)

    def _build_search_params(
        self,
        limit: int,
        collection: str | None = None,
        show_ranking_score: bool = True,
    ) -> dict:
        """Build common search parameters."""
        params = {
            'limit': limit,
            'showRankingScore': show_ranking_score,
            'attributesToHighlight': ['content'],
            'highlightPreTag': '<<',
            'highlightPostTag': '>>',
        }
        if collection:
            params['filter'] = f'collection = "{collection}"'
        return params

    def _to_search_result(self, result: dict) -> SearchResult:
        """Convert Meilisearch result to SearchResult."""
        hits = []
        for hit in result.get('hits', []):
            hits.append(
                SearchHit(
                    id=hit.get('id', ''),
                    score=hit.get('_rankingScore', 0.0),
                    document=hit,
                )
            )
        return SearchResult(
            hits=hits,
            processing_time_ms=result.get('processingTimeMs', 0),
            total_hits=result.get('estimatedTotalHits'),
        )

    def bm25_search(
        self,
        collection: str,
        query: str,
        limit: int = 10,
        filter: dict | None = None,
    ) -> SearchResult:
        """Pure BM25 full-text search."""
        index = self._get_index()
        params = self._build_search_params(limit=limit, collection=collection)
        result = index.search(query, params)
        return self._to_search_result(result)

    def vector_search(
        self,
        collection: str,
        vector: list[float],
        limit: int = 10,
        filter: dict | None = None,
    ) -> SearchResult:
        """Pure vector search (semanticRatio=1.0)."""
        index = self._get_index()
        params = self._build_search_params(limit=limit, collection=collection)
        params['hybrid'] = {
            'embedder': 'gemma',
            'semanticRatio': 1.0,
        }
        params['vector'] = vector
        result = index.search('', params)
        return self._to_search_result(result)

    def hybrid_search(
        self,
        collection: str,
        query: str,
        vector: list[float],
        limit: int = 10,
        semantic_ratio: float = 0.5,
        filter: dict | None = None,
    ) -> SearchResult:
        """Hybrid search combining BM25 and vector."""
        index = self._get_index()
        params = self._build_search_params(limit=limit, collection=collection)
        params['hybrid'] = {
            'embedder': 'gemma',
            'semanticRatio': semantic_ratio,
        }
        params['vector'] = vector
        result = index.search(query, params)
        return self._to_search_result(result)

    def get_document(self, collection: str, id: str) -> dict | None:
        """Get a single document by ID."""
        index = self._get_index()
        doc = index.get_document(id)
        if doc:
            return dict(doc)
        return None

    def get_documents(
        self,
        collection: str,
        limit: int = 1000,
        offset: int = 0,
        filter: str | None = None,
    ) -> list[dict]:
        """Get documents with pagination."""
        index = self._get_index()
        params: dict = {'limit': limit, 'offset': offset}
        if filter:
            params['filter'] = filter
        results = index.get_documents(params)
        return [dict(d) for d in results.results]

    def get_stats(self, collection: str) -> dict:
        """Get collection statistics."""
        index = self._get_index()
        stats = index.get_stats()
        return {
            'total_documents': stats.number_of_documents,
            'is_indexing': stats.is_indexing,
        }

    def health_check(self) -> dict:
        """Health check."""
        health = self.client.health()
        return {
            'status': health.get('status', 'unknown'),
            'backend': 'meilisearch',
            'url': self.url,
        }
