"""Meilisearch integration for sub-100ms catalog search.

Provides a thin wrapper over the meilisearch Python client with:
- Automatic index creation and settings configuration
- Bulk indexing from PostgreSQL
- Search with graceful fallback to PostgreSQL tsvector
- Incremental add/update/delete for real-time sync

Environment:
    MEILI_URL      — Meilisearch server URL (default: http://localhost:7700)
    MEILI_API_KEY  — Master key (default: nexus-master-key-change-me)
"""

import os
import meilisearch
from meilisearch.errors import MeilisearchApiError

MEILI_URL = os.environ.get('MEILI_URL', 'http://localhost:7700')
MEILI_API_KEY = os.environ.get('MEILI_API_KEY', 'nexus-master-key-change-me')
INDEX_NAME = 'nexus_parts'

# Searchable attributes and ranking
INDEX_SETTINGS = {
    'searchableAttributes': [
        'name_es',
        'name_part',
        'oem_part_number',
        'description',
        'description_es',
    ],
    'rankingRules': [
        'words',
        'typo',
        'proximity',
        'attribute',
        'sort',
        'exactness',
    ],
    'filterableAttributes': ['group_id'],
    'typoTolerance': {'enabled': True, 'minWordSizeForTypos': {'oneTypo': 4, 'twoTypos': 8}},
}

_client = None
_client_url = None


def get_client():
    """Get or create Meilisearch client (lazy singleton, URL-aware)."""
    global _client, _client_url
    current_url = os.environ.get('MEILI_URL', 'http://localhost:7700')
    if _client is None or _client_url != current_url:
        _client = meilisearch.Client(current_url, MEILI_API_KEY)
        _client_url = current_url
    return _client


def reset_client():
    """Force client recreation on next use (useful for tests)."""
    global _client, _client_url
    _client = None
    _client_url = None


def health_check():
    """Return True if Meilisearch is reachable."""
    try:
        return get_client().health().get('status') == 'available'
    except Exception:
        return False


def ensure_index():
    """Create index if it doesn't exist and configure settings."""
    client = get_client()
    try:
        client.get_index(INDEX_NAME)
    except MeilisearchApiError as e:
        if e.code == 'index_not_found':
            client.create_index(uid=INDEX_NAME, options={'primaryKey': 'id_part'})
        else:
            raise

    index = client.index(INDEX_NAME)
    index.update_settings(INDEX_SETTINGS)
    return index


def index_parts_bulk(parts_iter, batch_size=1000):
    """Index a large number of parts from an iterable.

    Args:
        parts_iter: iterable of dicts with keys:
            id_part, oem_part_number, name_part, name_es,
            description, description_es, image_url, group_id
        batch_size: documents per batch upload
    """
    index = ensure_index()
    batch = []
    total = 0
    for part in parts_iter:
        batch.append(part)
        if len(batch) >= batch_size:
            index.add_documents(batch)
            total += len(batch)
            batch = []
    if batch:
        index.add_documents(batch)
        total += len(batch)
    return total


def search_parts(query, limit=50, offset=0):
    """Search parts via Meilisearch.

    Returns:
        dict: Meilisearch response with 'hits', 'offset', 'limit', 'totalHits'
        or None on error.
    """
    try:
        index = get_client().index(INDEX_NAME)
        return index.search(query, {'limit': limit, 'offset': offset})
    except Exception as e:
        print(f"[meili_search] Search error: {e}")
        return None


def add_part(part_doc):
    """Add or update a single part document."""
    try:
        get_client().index(INDEX_NAME).add_documents([part_doc])
        return True
    except Exception as e:
        print(f"[meili_search] Add error: {e}")
        return False


def update_part(part_doc):
    """Update a single part document (same as add)."""
    return add_part(part_doc)


def delete_part(part_id):
    """Remove a part from the index."""
    try:
        get_client().index(INDEX_NAME).delete_document(part_id)
        return True
    except Exception as e:
        print(f"[meili_search] Delete error: {e}")
        return False


def clear_index():
    """Delete all documents from the index."""
    try:
        get_client().index(INDEX_NAME).delete_all_documents()
        return True
    except Exception as e:
        print(f"[meili_search] Clear error: {e}")
        return False
