"""Launch and systems status — explicit startup sequence for background services.

Services are created during server lifespan but NOT auto-started.
The user triggers launch explicitly via POST /api/launch (or the dashboard
Launch button). This gives a visible startup sequence and confidence that
all systems are operational.
"""

from __future__ import annotations

import asyncio
import logging
import time

from fastapi import APIRouter, Request

log = logging.getLogger(__name__)

router = APIRouter(tags=["launch"])


def _persist_auto_launch(request: Request) -> None:
    """After first manual launch, persist auto_launch=true so next boot auto-starts."""
    try:
        from agntspace.infra.settings_service import SettingsService

        settings = request.app.state.settings
        svc = SettingsService(settings.config_path)
        current = svc.get_section("schedule")
        if not current.get("auto_launch"):
            svc.set_value("schedule", "auto_launch", True)
            log.info("Auto-launch enabled — background services will start automatically on next boot")
    except Exception:
        log.debug("Could not persist auto_launch setting", exc_info=True)

# Ordered list of (display_name, app.state attribute, check_function_or_None)
_SERVICES = [
    ("vault", None, None),  # always OK if we got this far
    ("llm", "llm", "_check_llm"),
    ("knowledge_graph", "knowledge_graph", None),
    ("memory", "memory_engine", None),
    ("work_queue", "work_queue", None),
    ("heartbeat", "heartbeat", None),
    ("scheduler", "scheduler", None),
    ("daily_cycle", "daily_cycle", None),
    ("cron", "cron", None),
    ("watchers", "raw_watcher", None),
]


async def _check_llm(request: Request) -> dict:
    """Ping the LLM provider and return model info."""
    llm = getattr(request.app.state, "llm", None)
    if not llm:
        return {"ok": False, "error": "No LLM provider configured"}
    try:
        healthy = await asyncio.wait_for(llm.check_health(), timeout=10.0)
        model = getattr(llm, "model_name", None) or "unknown"
        if healthy:
            return {"ok": True, "model": model}
        return {"ok": False, "error": getattr(llm, "last_error", "unhealthy"), "model": model}
    except TimeoutError:
        return {"ok": False, "error": "LLM health check timed out (10s)"}
    except Exception as exc:
        return {"ok": False, "error": str(exc)}


@router.post("/launch")
async def launch(request: Request) -> dict:
    """Start all background services in sequence. Returns status per service."""
    if getattr(request.app.state, "launched", False):
        return {"status": "already_launched", "systems": await _get_systems(request)}

    results = []
    t0 = time.monotonic()

    # 1. Vault — always OK if server booted
    results.append({"name": "vault", "status": "ok", "detail": "loaded"})

    # 2. LLM — ping the provider
    llm_result = await _check_llm(request)
    results.append({
        "name": "llm",
        "status": "ok" if llm_result.get("ok") else "warning",
        "detail": llm_result.get("model", llm_result.get("error", "")),
    })

    # 3. Knowledge graph stats
    graph = getattr(request.app.state, "knowledge_graph", None)
    if graph:
        stats = graph.get_stats() if hasattr(graph, "get_stats") else {}
        entities = stats.get("entity_count", 0)
        triples = stats.get("triple_count", 0)
        results.append({"name": "knowledge_graph", "status": "ok", "detail": f"{entities} entities, {triples} triples"})
    else:
        results.append({"name": "knowledge_graph", "status": "skip", "detail": "not initialized"})

    # 4. Memory layers
    mem = getattr(request.app.state, "memory_engine", None)
    if mem:
        results.append({"name": "memory", "status": "ok", "detail": "6 layers"})
    else:
        results.append({"name": "memory", "status": "skip", "detail": "not initialized"})

    # 5-10. Start background services in order
    _start_order = [
        ("work_queue", "Work queue"),
        ("heartbeat", "Heartbeat"),
        ("scheduler", "Task scheduler"),
        ("daily_cycle", "Daily cycle"),
        ("cron", "Cron scheduler"),
        ("raw_watcher", "Inbox watcher"),
        ("vault_edit_watcher", "Vault edit watcher"),
    ]

    for attr, label in _start_order:
        svc = getattr(request.app.state, attr, None)
        if svc and hasattr(svc, "start"):
            try:
                svc.start()
                results.append({"name": attr, "status": "ok", "detail": "started"})
            except Exception as exc:
                results.append({"name": attr, "status": "error", "detail": str(exc)})
                log.exception("Failed to start %s", attr)
        else:
            results.append({"name": attr, "status": "skip", "detail": "not available"})

    # Start filesystem event bridge if available
    fs_bridge = getattr(request.app.state, "fs_bridge", None)
    if fs_bridge and hasattr(fs_bridge, "start"):
        if fs_bridge.start():
            log.info("Real-time file watching enabled (watchdog)")
        else:
            log.info("Real-time file watching unavailable — polling-only mode")

    # Channels
    cm = getattr(request.app.state, "channel_manager", None)
    if cm:
        channels = cm.list_channels() if hasattr(cm, "list_channels") else []
        connected = [c for c in channels if c.get("connected")]
        results.append({
            "name": "channels",
            "status": "ok" if connected else "skip",
            "detail": f"{len(connected)} connected" if connected else "none configured",
        })

    elapsed_ms = int((time.monotonic() - t0) * 1000)

    request.app.state.launched = True
    request.app.state.frozen = False

    # Persist auto_launch on first manual launch so next boot auto-starts
    _persist_auto_launch(request)

    log.info("LAUNCH completed in %dms — all systems started", elapsed_ms)

    # Activity event
    activity = getattr(request.app.state, "activity_logger", None)
    if activity:
        activity.log(
            category="system",
            action="launch",
            summary=f"All systems launched ({elapsed_ms}ms)",
            importance="high",
        )

    return {
        "status": "launched",
        "elapsed_ms": elapsed_ms,
        "systems": results,
    }


@router.get("/systems/status")
async def systems_status(request: Request) -> dict:
    """Current status of all subsystems."""
    return {"launched": getattr(request.app.state, "launched", False), "systems": await _get_systems(request)}


async def _get_systems(request: Request) -> list[dict]:
    """Build current status for all subsystems."""
    results = []

    # Vault
    results.append({"name": "vault", "status": "ok", "detail": "loaded"})

    # LLM
    llm = getattr(request.app.state, "llm", None)
    if llm:
        healthy = getattr(llm, "healthy", None)
        model = getattr(llm, "model_name", None) or "unknown"
        if healthy:
            results.append({"name": "llm", "status": "ok", "detail": model})
        elif healthy is False:
            err = getattr(llm, "last_error", "unhealthy")
            results.append({"name": "llm", "status": "warning", "detail": f"{model} — {err}"})
        else:
            results.append({"name": "llm", "status": "unknown", "detail": model})
    else:
        results.append({"name": "llm", "status": "warning", "detail": "not configured"})

    # Knowledge graph
    graph = getattr(request.app.state, "knowledge_graph", None)
    if graph:
        stats = graph.get_stats() if hasattr(graph, "get_stats") else {}
        entities = stats.get("entity_count", 0)
        triples = stats.get("triple_count", 0)
        results.append({"name": "knowledge_graph", "status": "ok", "detail": f"{entities} entities, {triples} triples"})
    else:
        results.append({"name": "knowledge_graph", "status": "skip", "detail": "not initialized"})

    # Memory
    mem = getattr(request.app.state, "memory_engine", None)
    if mem:
        results.append({"name": "memory", "status": "ok", "detail": "6 layers"})
    else:
        results.append({"name": "memory", "status": "skip", "detail": "not initialized"})

    # Background services — check _running flag
    _bg_services = [
        ("work_queue", "Work queue"),
        ("heartbeat", "Heartbeat"),
        ("scheduler", "Task scheduler"),
        ("daily_cycle", "Daily cycle"),
        ("cron", "Cron scheduler"),
        ("raw_watcher", "Inbox watcher"),
        ("vault_edit_watcher", "Vault edit watcher"),
    ]
    for attr, label in _bg_services:
        svc = getattr(request.app.state, attr, None)
        if not svc:
            results.append({"name": attr, "status": "skip", "detail": "not available"})
            continue
        # Services use either _running (bool) or _stop (asyncio.Event, inverted)
        _stop = getattr(svc, "_stop", None)
        running = getattr(svc, "_running", None) or getattr(svc, "is_running", None)
        if running is None and _stop is not None:
            running = not _stop.is_set()
        # Also check if the task exists and isn't done
        if not running:
            task = getattr(svc, "_task", None)
            if task and not task.done():
                running = True
        if running:
            results.append({"name": attr, "status": "ok", "detail": "running"})
        else:
            results.append({"name": attr, "status": "stopped", "detail": "not started"})

    # Channels
    cm = getattr(request.app.state, "channel_manager", None)
    if cm:
        channels = cm.list_channels() if hasattr(cm, "list_channels") else []
        connected = [c for c in channels if c.get("connected")]
        results.append({
            "name": "channels",
            "status": "ok" if connected else "skip",
            "detail": f"{len(connected)} connected" if connected else "none configured",
        })

    return results
