"""
Thin MongoDB write layer for the afs_world_models collection (Spec 24 P2).

Writes derived output world models from MCP runs so they are available to
corpusService.js Jaccard search (Spec 24 P4 corpus pre-check in afsHeadlessService).

Uses SHARE_MONGO_URI — same production MongoDB as shares. Corpus artifacts
from MCP runs are immediately accessible to the JS headless service.

Graceful degradation: if MongoDB is unavailable, returns silently. The caller
(afs_decide / afs_followup) is never blocked or interrupted.

Schema mirrors WorldModelArtifact.js. Documents without network_id (MCP source)
are treated as standalone corpus artifacts — corpusService.js queries do not
filter on network_id.

Usage:
    from afs.store.world_model_store import store_world_model

    store_world_model(output_wm, question, factor_names)
    # returns None — fire-and-forget, never raises
"""

import os
import logging
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
_ttl_ensured = False

_TTL_SECONDS = 60 * 60 * 24 * 90  # 90 days


def _get_collection():
    """Return the afs_world_models Collection, or None if MongoDB is unavailable."""
    global _client, _collection, _unavailable, _ttl_ensured

    if _unavailable:
        return None

    if _collection is not None:
        return _collection

    uri = os.environ.get("SHARE_MONGO_URI")
    if not uri:
        logger.warning(
            "[world_model_store] SHARE_MONGO_URI not set — corpus artifacts will not be persisted"
        )
        _unavailable = True
        return None

    try:
        from pymongo import MongoClient, ASCENDING

        _client = MongoClient(uri, serverSelectionTimeoutMS=3000)
        _client.admin.command("ping")

        db = _client.get_default_database()
        _collection = db["afs_world_models"]

        if not _ttl_ensured:
            try:
                # TTL index: auto-expire 90 days after created_at (mirrors WorldModelArtifact.js)
                _collection.create_index(
                    [("created_at", ASCENDING)],
                    expireAfterSeconds=_TTL_SECONDS,
                    name="ttl_created_at",
                    background=True,
                )
            except Exception as _idx_err:
                # Index may already exist with different name — non-fatal
                logger.debug("[world_model_store] TTL index: %s", _idx_err)
            try:
                # Compound index for efficient per-module history queries from JS side
                _collection.create_index(
                    [("network_id", ASCENDING), ("module_id", ASCENDING), ("created_at", ASCENDING)],
                    name="network_module_time",
                    background=True,
                )
            except Exception as _idx_err:
                logger.debug("[world_model_store] compound index: %s", _idx_err)
            _ttl_ensured = True

        logger.info(
            "[world_model_store] Connected — using %s.afs_world_models", db.name
        )
        return _collection

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

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


def store_world_model(world_model: dict, question: str, factor_names: list) -> None:
    """Fire-and-forget: persist a derived output world model to afs_world_models.

    Documents written here are picked up by corpusService.js Jaccard search
    (Spec 24 P4) on subsequent afs_decide calls routed through afsHeadlessService.

    Args:
        world_model: Derived WM dict from output_to_world_model() — must have
                     factors, options, relationships, _derived=True.
        question:    Originating question string (stored as source_label for display).
        factor_names: List of factor name strings for P4 Jaccard search index.

    Returns:
        None. Never raises — failures are logged and swallowed.
    """
    col = _get_collection()
    if col is None:
        return

    doc = {
        "network_id": None,          # MCP run — no Living Network context
        "module_id": "mcp",
        "session_id": None,
        "world_model": world_model,
        "factor_names": [str(n) for n in (factor_names or [])],
        "strategy_count": world_model.get("_strategy_count", 0),
        "source_label": question,
        "source_type": "mcp",
        "corpus_layer": "L1",
        "created_at": datetime.now(timezone.utc),
    }

    try:
        col.insert_one(doc)
        logger.debug(
            "[world_model_store] Stored corpus artifact — %d factors, %d strategies",
            len(factor_names or []),
            doc["strategy_count"],
        )
    except Exception as exc:
        logger.warning(
            "[world_model_store] Failed to store corpus artifact: %s", exc
        )


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