"""
Qdrant sink for extractor.

Upserts chunk records as Qdrant points. For use with vector search RAG pipelines.
NOTE: This sink does NOT generate embeddings — it stores the text payload only.
Use a separate embedding step to populate the vector field after insertion,
or integrate with your embedding pipeline.

Auth (env vars):
    EXTRACTOR_QDRANT_URL      (default: http://localhost:6333)
    EXTRACTOR_QDRANT_API_KEY  (Qdrant Cloud API key)

Install: pip install 'coresdk-extractor[qdrant]'
"""

from __future__ import annotations

import contextlib
import logging
import os
import uuid
from typing import Any

from .base import BaseSink
from .registry import register_sink

logger = logging.getLogger(__name__)


class QdrantSink(BaseSink):
    def __init__(
        self,
        url: str | None = None,
        collection: str = "extractor_chunks",
        batch_size: int = 100,
        vector_size: int = 1536,  # matches text-embedding-3-small; override as needed
    ) -> None:
        self.url = url or os.environ.get("EXTRACTOR_QDRANT_URL", "http://localhost:6333")
        self.collection = collection
        self.batch_size = batch_size
        self.vector_size = vector_size
        self._client = None
        self._buffer: list[dict[str, Any]] = []
        self._total_inserted = 0

    def connect(self) -> None:
        try:
            from qdrant_client import QdrantClient
            from qdrant_client.models import Distance, VectorParams
        except ImportError as e:
            raise ImportError(
                "qdrant-client not installed. Run: pip install 'coresdk-extractor[qdrant]'"
            ) from e
        api_key = os.environ.get("EXTRACTOR_QDRANT_API_KEY")
        self._client = QdrantClient(url=self.url, api_key=api_key)
        # Create collection if not exists (zero vector placeholder — user populates embeddings)
        collections = {c.name for c in self._client.get_collections().collections}
        if self.collection not in collections:
            self._client.create_collection(
                collection_name=self.collection,
                vectors_config=VectorParams(size=self.vector_size, distance=Distance.COSINE),
            )
        logger.info("QdrantSink connected: %s/%s", self.url, self.collection)

    def feed(self, record: dict[str, Any]) -> None:
        self._buffer.append(record)
        if len(self._buffer) >= self.batch_size:
            self.write_batch(self._buffer)
            self._buffer = []

    def write_batch(self, records: list[dict[str, Any]]) -> None:
        if not records or self._client is None:
            return
        from qdrant_client.models import PointStruct

        # Payload: flat filterable fields only (no nested dicts for compat)
        points = []
        for r in records:
            rec_id = r.get("id", "")
            # Use deterministic UUID from element id for Qdrant point id
            point_id = str(uuid.uuid5(uuid.NAMESPACE_DNS, rec_id))
            payload = {
                "extractor_id": rec_id,
                "element_type": r.get("element_type", ""),
                "text": r.get("text", ""),
                "source_filename": r.get("source_filename", ""),
                "source_sha256": r.get("source_sha256", ""),
                "source_uri": r.get("source_uri"),
                "source_media_type": r.get("source_media_type", ""),
                "page": r.get("page"),
                "token_count": r.get("token_count"),
                "doc_category": r.get("doc_category"),
                "doc_category_score": r.get("doc_category_score"),
                "section_path": r.get("section_path", []),
                "entity_types": (r.get("chunk_metadata") or {}).get("entity_types") or [],
                "relation_subjects": r.get("relation_subjects") or [],
                "relation_objects": r.get("relation_objects") or [],
            }
            # Zero vector placeholder — caller replaces with real embeddings
            points.append(
                PointStruct(
                    id=point_id,
                    vector=[0.0] * self.vector_size,
                    payload={k: v for k, v in payload.items() if v is not None},
                )
            )
        self._client.upsert(collection_name=self.collection, points=points)
        self._total_inserted += len(points)

    def flush(self) -> None:
        if self._buffer:
            self.write_batch(self._buffer)
            self._buffer = []

    def close(self) -> None:
        if self._client is not None:
            with contextlib.suppress(Exception):
                self._client.close()
        self._client = None

    @property
    def total_inserted(self) -> int:
        return self._total_inserted


register_sink("qdrant", QdrantSink)
