#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
PULSE API — Agents router. Endpoints for agent registry, workers, and system overview.
"""
from __future__ import annotations

import json
from datetime import datetime, timezone
from pathlib import Path
from fastapi import APIRouter

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent
STATE_DIR = ROOT_DIR / "state"
# S38: hippo path constant — prevents NameError at line 186
hippo = ROOT_DIR / "Hippocampus"


from pulse.core.brain_utils import (
    load_json_dict, AGENT_REGISTRY_FILE, TASK_BOARD_FILE,
    read_jsonl_entries, LESSONS_JSONL, FAILS_JSONL, PATTERNS_JSONL,
)

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


STALE_THRESHOLD_SECONDS = 3600  # 1 hour — no heartbeat = offline

# Map internal status names → frontend-expected names
_STATUS_MAP = {"working": "running", "offline": "dead", "booting": "idle"}


def _resolve_status(data: dict) -> str:
    """Return real status based on heartbeat freshness. Maps to frontend values."""
    raw_status = data.get("status", "unknown")
    hb = data.get("last_heartbeat")
    if not hb:
        status = "offline" if raw_status in ("idle", "working", "booting") else raw_status
        return _STATUS_MAP.get(status, status)
    try:
        hb_time = datetime.fromisoformat(hb)
        if hb_time.tzinfo is None:
            hb_time = hb_time.replace(tzinfo=timezone.utc)
        age = (datetime.now(timezone.utc) - hb_time).total_seconds()
        if age > STALE_THRESHOLD_SECONDS and raw_status in ("idle", "working", "booting"):
            return _STATUS_MAP.get("offline", "dead")
    except (ValueError, TypeError):
        pass
    return _STATUS_MAP.get(raw_status, raw_status)


def _infer_type(agent_id: str) -> str:
    """Fallback for old registry entries without type field."""
    if agent_id.endswith("_daemon"):
        return "daemon"
    if agent_id.startswith("worker_"):
        return "worker"
    return "agent"


def _is_real_agent(agent_id: str, data: dict) -> bool:
    """Filter to only real agents using type field (fallback to heuristic)."""
    return data.get("type", _infer_type(agent_id)) == "agent"



@router.get("/v1/agents")
async def get_agents(include_dead: bool = False):
    """Return registered agents (excludes daemons and workers).
    By default only returns alive agents. Pass ?include_dead=true for all."""
    registry = load_json_dict(AGENT_REGISTRY_FILE)
    agents = []
    for agent_id, data in registry.items():
        if not _is_real_agent(agent_id, data):
            continue
        status = _resolve_status(data)
        if not include_dead and status == "dead":
            continue
        agents.append({
            "id": agent_id,
            "model": data.get("model", "unknown"),
            "tier": data.get("tier", "unknown"),
            "type": data.get("type", _infer_type(agent_id)),
            "status": status,
            "current_task": data.get("current_task"),
            "last_heartbeat": data.get("last_heartbeat"),
            "capabilities": data.get("capabilities", []),
            "history": data.get("history", {}),
        })
    return {"count": len(agents), "agents": agents}


@router.get("/v1/agents/{agent_id}/heartbeat")
async def get_agent_heartbeat(agent_id: str):
    """Return latest heartbeat for a specific agent."""
    hb_file = STATE_DIR / "heartbeats" / f"{agent_id}.json"
    if not hb_file.exists():
        return {"agent_id": agent_id, "status": "no_heartbeat"}
    data = json.loads(hb_file.read_text(encoding="utf-8"))
    return data


@router.get("/v1/agents/{agent_id}/procedures")
async def get_agent_procedures(agent_id: str):
    """Return agent-specific procedure recommendations based on success/failure history."""
    from pulse.brain.metacognition.agent_procedures import (
        get_agent_profile, match_procedures, get_weak_domains, get_strong_domains,
    )
    profile = get_agent_profile(agent_id)
    if not profile:
        return {"agent_id": agent_id, "status": "not_found"}
    return {
        "agent_id": agent_id,
        "profile": profile,
        "weak_domains": get_weak_domains(agent_id),
        "strong_domains": get_strong_domains(agent_id),
        "procedures": match_procedures(agent_id),
    }


@router.get("/v1/workers")
async def get_workers(include_dead: bool = False):
    """Return swarm workers. Uses shared get_swarm_data() — same source as CLI.
    By default only returns alive workers. Pass ?include_dead=true for all."""
    from pulse.workers.runner.worker_state import get_swarm_data
    data = get_swarm_data()
    workers = data["workers"]
    if not include_dead:
        workers = [w for w in workers if w["status"] != "dead"]
    return {"count": len(workers), "workers": workers}

