from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
from pymongo.collection import Collection

from bson.codec_options import CodecOptions

from app.core.config import settings

_client: AsyncIOMotorClient | None = None
_TOLERANT_CODEC = CodecOptions(unicode_decode_error_handler="replace")


def get_client() -> AsyncIOMotorClient:
    """Return a process-local Motor client (safe for Celery fork workers)."""
    global _client
    if _client is None:
        _client = AsyncIOMotorClient(settings.MONGO_URI)
    return _client


def reset_client() -> None:
    """Close and drop the cached client so the next use binds to a fresh event loop."""
    global _client
    if _client is not None:
        _client.close()
    _client = None


def get_db() -> AsyncIOMotorDatabase:
    """Return the embedding-engine MongoDB database (embedding_records, embedding_runs)."""
    return get_client()[settings.DATABASE_NAME]


def get_docs_db() -> AsyncIOMotorDatabase:
    """Return the database holding upstream doc_chunks (Docs Ingestion)."""
    return get_client()[settings.resolved_docs_database()]


def get_code_chunks_db() -> AsyncIOMotorDatabase:
    """Return the database holding upstream code_chunks (Code Parser)."""
    return get_client()[settings.resolved_code_chunks_database()]


def get_commit_analyses_db() -> AsyncIOMotorDatabase:
    """Return the database holding upstream commit_analyses (Commit Intelligence)."""
    return get_client()[settings.resolved_commit_analyses_database()]


def get_indexing_runs_db() -> AsyncIOMotorDatabase:
    """Return the database holding shared indexing_runs (Repo Sync)."""
    return get_client()[settings.INDEXING_RUNS_DATABASE]


def get_upstream_collection(db: AsyncIOMotorDatabase, name: str) -> Collection:
    """
    Return a collection configured to tolerate invalid UTF-8 in upstream BSON strings.

    Code parser may persist binary file payloads (for example .avif assets) as chunk text.
    """
    return db.get_collection(name, codec_options=_TOLERANT_CODEC)


async def is_db_ready() -> bool:
    """Ping MongoDB for readiness checks."""
    try:
        await get_db().command("ping")
        return True
    except Exception:
        return False
