"""
Thin MongoDB read/write layer for the afs_ablations collection.

Graceful degradation: if MongoDB is unavailable (not installed, URI missing, or
connection refused), all writes are silently dropped with a warning log and reads
return None. Callers must not rely on persistence for correctness — the ablation
delta is always returned in-process regardless of storage outcome.

Usage:
    from afs.store.ablation_store import store_ablation, get_ablation

    ablation_id = store_ablation({
        "question": "...",
        "option_count": 3,
        "factor_count": 8,
        "relationship_count": 5,
        "with_matrix":    {"strategy_count": 3, "separation": 0.42, "spread": 0.31},
        "without_matrix": {"strategy_count": 1, "separation": 0.18, "spread": 0.29},
        "delta": {
            "strategy_count_diff": 2,
            "centroid_displacement_mean": 0.24,
            "separation_gain": 0.24,
            "concept_weight_shift_max": 0.18,
            "matrix_mattered": True,
        },
    })

    doc = get_ablation(ablation_id)   # returns full record or None
"""

import os
import logging
import uuid
from datetime import datetime, timezone
from typing import Optional

logger = logging.getLogger(__name__)

# Module-level connection state.  Initialized lazily on first call.
# _reset() clears these for test isolation.
_client = None
_collection = None
_unavailable = False   # once we know DB is unreachable, skip further attempts


def _get_collection():
    """Return the afs_ablations Collection, or None if MongoDB is unavailable.

    Connects once per process lifetime. Subsequent calls reuse the cached
    collection.  Once connection fails, _unavailable is set and no further
    attempts are made within this process.
    """
    global _client, _collection, _unavailable

    if _unavailable:
        return None

    if _collection is not None:
        return _collection

    uri = os.environ.get("MONGODB_LOCAL_URI")
    if not uri:
        logger.warning(
            "[ablation_store] MONGODB_LOCAL_URI not set — ablations will not be persisted"
        )
        _unavailable = True
        return None

    try:
        from pymongo import MongoClient

        _client = MongoClient(uri, serverSelectionTimeoutMS=3000)
        _client.admin.command("ping")   # fast connectivity check — raises on failure

        db = _client.get_default_database()
        _collection = db["afs_ablations"]
        logger.info(
            "[ablation_store] Connected — using %s.afs_ablations", db.name
        )
        return _collection

    except ImportError:
        logger.warning(
            "[ablation_store] pymongo not installed — ablations will not be persisted. "
            "Install with: pip install pymongo"
        )
        _unavailable = True
        return None

    except Exception as exc:
        logger.warning(
            "[ablation_store] MongoDB unavailable (%s) — ablations will not be persisted",
            exc,
        )
        _unavailable = True
        return None


def store_ablation(record: dict) -> str:
    """Persist an ablation record to afs_ablations.

    Assigns a fresh UUID ablation_id if the record doesn't already have one.
    Adds created_at timestamp.  Returns the ablation_id whether or not
    persistence succeeded.

    Args:
        record: Dict matching the afs_ablations schema (see module docstring).
                Must not contain an _id field — MongoDB assigns one.

    Returns:
        ablation_id (str): UUID identifying this ablation run.
    """
    ablation_id = record.get("ablation_id") or str(uuid.uuid4())

    doc = {
        **record,
        "ablation_id": ablation_id,
        "created_at": datetime.now(timezone.utc),
    }
    # Don't let a caller-supplied _id cause duplicate-key errors
    doc.pop("_id", None)

    col = _get_collection()
    if col is not None:
        try:
            col.insert_one(doc)
        except Exception as exc:
            logger.warning(
                "[ablation_store] Failed to insert ablation %s: %s", ablation_id, exc
            )

    return ablation_id


def get_ablation(ablation_id: str) -> Optional[dict]:
    """Retrieve an ablation record by ablation_id.

    Returns:
        The stored document (without _id) or None if not found / DB unavailable.
    """
    col = _get_collection()
    if col is None:
        return None
    try:
        return col.find_one({"ablation_id": ablation_id}, {"_id": 0})
    except Exception as exc:
        logger.warning(
            "[ablation_store] Failed to retrieve ablation %s: %s", ablation_id, exc
        )
        return None


def _reset() -> None:
    """Reset connection state.  Call between tests that manipulate env vars or mock _get_collection."""
    global _client, _collection, _unavailable
    if _client is not None:
        try:
            _client.close()
        except Exception:
            pass
    _client = None
    _collection = None
    _unavailable = False
