"""Neo4j driver lifecycle and session dependency for FastAPI."""

import logging
from collections.abc import Generator
from contextlib import asynccontextmanager

from fastapi import FastAPI
from neo4j import Driver, GraphDatabase, Session

from app.core.config import get_settings
from app.services.chatbot.langgraph_orchestrator import LangGraphPRALOrchestrator

logger = logging.getLogger(__name__)

_driver: Driver | None = None


def _init_driver() -> None:
    """Initialize the global Neo4j driver singleton.

    Raises:
        RuntimeError: When driver initialization fails.
    """
    global _driver
    settings = get_settings()
    _driver = GraphDatabase.driver(
        settings.neo4j_uri,
        auth=(settings.neo4j_username, settings.neo4j_password),
    )
    _driver.verify_connectivity()
    logger.info("Neo4j driver initialized", extra={"uri": settings.neo4j_uri})


def _close_driver() -> None:
    """Close the global Neo4j driver and release connection pool resources."""
    global _driver
    if _driver is not None:
        _driver.close()
        _driver = None
        logger.info("Neo4j driver closed")


def refresh_driver() -> None:
    """Rebuild the global Neo4j driver singleton.

    This helper is intended for runtime self-heal paths when an active Neo4j
    connection becomes defunct and the application needs to recreate the pool
    before retrying a request.

    Warning:
        Do not call this from ordinary concurrent request paths or read-level
        self-heal flows. This closes the process-wide driver singleton and can
        invalidate long-running background work that is still using sessions
        created from the previous driver instance.

    Raises:
        RuntimeError: When the driver cannot be initialized.
    """
    # This is only safe for controlled recovery paths where no active workload
    # still depends on the current driver instance.
    _close_driver()
    _init_driver()


def get_driver() -> Driver:
    """Return the initialized global Neo4j driver singleton."""
    if _driver is None:
        raise RuntimeError("Neo4j driver is not initialized.")
    return _driver


@asynccontextmanager
async def neo4j_lifespan(app: FastAPI):
    """Manage Neo4j driver lifecycle tied to FastAPI application lifespan."""
    _init_driver()

    logger.info("Initializing LangGraph PRAL Orchestrator for Chatbot...")
    orchestrator = None
    try:
        orchestrator = LangGraphPRALOrchestrator()
        await orchestrator.connect()
        app.state.orchestrator = orchestrator
        logger.info("LangGraph PRAL Orchestrator initialized and attached to app state")
    except Exception as exc:
        logger.error("Failed to initialize LangGraph PRAL Orchestrator: %s", exc)
        app.state.orchestrator = None

    yield

    if getattr(app.state, "orchestrator", None):
        await app.state.orchestrator.close()
        logger.info("LangGraph PRAL Orchestrator connection closed")

    _close_driver()


def get_db_session() -> Generator[Session, None, None]:
    """Provide a request-scoped Neo4j session via FastAPI dependency injection."""
    if _driver is None:
        raise RuntimeError("Neo4j driver is not initialized.")
    session = _driver.session()
    try:
        yield session
    finally:
        session.close()
