from __future__ import annotations

from typing import List, Optional

from analogai.foundation.settings import config as infra_config
from analogai.foundation.modules.fact.source.fact_source import FactSource, SourceType
from analogai.foundation.modules.fact.source.repositories.fact_source_repository import FactSourceRepository


class MongoFactSourceRepository(FactSourceRepository):
    def __init__(
        self,
        uri: Optional[str] = None,
        database: Optional[str] = None,
        sources_collection: str = "fact_sources",
    ) -> None:
        try:
            from pymongo import MongoClient
        except Exception as exc:  # pragma: no cover - optional dependency
            raise ImportError("pymongo is required for MongoFactSourceRepository") from exc
        mongo_uri = uri or infra_config.get("MONGODB_URI", "")
        mongo_db = database or infra_config.get("MONGODB_DATABASE", "")
        if not mongo_uri or not mongo_db:
            raise ValueError("MongoDB configuration missing (MONGODB_URI/MONGODB_DATABASE)")
        client = MongoClient(mongo_uri)
        db = client[mongo_db]
        self._sources = db[sources_collection]
        self._ensure_indexes()

    def _ensure_indexes(self) -> None:
        try:
            self._sources.create_index([("_id", 1)], unique=True, background=True)
            self._sources.create_index([("agent_id", 1)], background=True)
            self._sources.create_index([("source_type", 1)], background=True)
            self._sources.create_index([("source_value", 1)], background=True)
        except Exception:
            pass

    def register_source(self, source: FactSource) -> None:
        if source is None:
            return
        if source.value is None or str(source.value).strip() == "":
            return
        if source.agent_id is None or str(source.agent_id).strip() == "":
            return
        doc = {
            "_id": source.id,
            "agent_id": source.agent_id,
            "source_type": source.type.value,
            "source_value": source.value,
        }
        self._sources.update_one({"_id": source.id}, {"$set": doc}, upsert=True)

    def get_source(self, source_id: str) -> Optional[FactSource]:
        if not source_id:
            return None
        doc = self._sources.find_one({"_id": source_id})
        return _source_from_doc(doc)

    def list_sources(self) -> List[FactSource]:
        cursor = self._sources.find(
            {"source_type": {"$ne": None}},
            {"source_type": 1, "source_value": 1, "agent_id": 1},
        )
        results: List[FactSource] = []
        for doc in cursor:
            source = _source_from_doc(doc)
            if source is not None:
                results.append(source)
        return results

    def list_sources_for_agent(self, agent_id: str) -> List[FactSource]:
        if not agent_id:
            return []
        cursor = self._sources.find(
            {"agent_id": agent_id, "source_type": {"$ne": None}},
            {"source_type": 1, "source_value": 1, "agent_id": 1},
        )
        results: List[FactSource] = []
        for doc in cursor:
            source = _source_from_doc(doc)
            if source is not None:
                results.append(source)
        return results

    def delete_source(self, source_id: str) -> Optional[FactSource]:
        if not source_id:
            return None
        doc = self._sources.find_one({"_id": source_id})
        if doc is None:
            return None
        self._sources.delete_one({"_id": source_id})
        return _source_from_doc(doc)


def _source_from_doc(doc: dict | None) -> Optional[FactSource]:
    if not doc:
        return None
    agent_id = doc.get("agent_id")
    source_type = doc.get("source_type")
    source_value = doc.get("source_value")
    if source_type is None or source_value is None:
        return None
    try:
        stype = SourceType(str(source_type))
    except Exception:
        try:
            stype = SourceType[str(source_type).upper()]
        except Exception:
            stype = SourceType.OTHER
    return FactSource(stype, str(source_value), str(agent_id) if agent_id else None)
