import json
import os
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional

import httpx
from fastapi import APIRouter
from redis.asyncio import Redis

router = APIRouter(prefix="/v1", tags=["daarion-facade"])

REGISTRY_CACHE_TTL = int(os.getenv("REGISTRY_CACHE_TTL", "30"))
MEMORY_SERVICE_URL = os.getenv("MEMORY_SERVICE_URL", "http://memory-service:8000")
CREWAI_SERVICE_URL = os.getenv("CREWAI_SERVICE_URL", "http://dagi-staging-crewai-service:9010")
REDIS_URL = os.getenv("REDIS_URL", "redis://redis:6379/0")
METRICS_DASHBOARD_KEY = "daarion:metrics:dashboard"

_REGISTRY_CACHE: Dict[str, Any] = {"loaded_at": 0.0, "data": None}
_DISTRICT_CACHE: Dict[str, Any] = {"loaded_at": 0.0, "data": None}
_CREWAI_CACHE: Dict[str, Any] = {"loaded_at": 0.0, "data": {}}
_REDIS: Optional[Redis] = None


def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


def _registry_paths() -> List[Path]:
    return [
        Path("/app/gateway-bot/agent_registry.json"),
        Path("/opt/microdao-daarion/config/agent_registry.json"),
        Path(__file__).resolve().parents[1] / "agent_registry.json",
    ]


def _district_paths() -> List[Path]:
    return [
        Path("/app/gateway-bot/district_registry.json"),
        Path(__file__).resolve().parents[1] / "district_registry.json",
    ]


def _load_registry() -> Dict[str, Any]:
    now = time.time()
    if _REGISTRY_CACHE.get("data") and (now - _REGISTRY_CACHE.get("loaded_at", 0.0) < REGISTRY_CACHE_TTL):
        return _REGISTRY_CACHE["data"]

    for path in _registry_paths():
        if path.exists():
            with path.open("r", encoding="utf-8") as f:
                data = json.load(f)
                _REGISTRY_CACHE.update({"loaded_at": now, "data": data})
                return data

    data = {"agents": {}}
    _REGISTRY_CACHE.update({"loaded_at": now, "data": data})
    return data


def _load_district_registry() -> Dict[str, Any]:
    now = time.time()
    if _DISTRICT_CACHE.get("data") and (now - _DISTRICT_CACHE.get("loaded_at", 0.0) < REGISTRY_CACHE_TTL):
        return _DISTRICT_CACHE["data"]

    for path in _district_paths():
        if path.exists():
            with path.open("r", encoding="utf-8") as f:
                data = json.load(f)
                _DISTRICT_CACHE.update({"loaded_at": now, "data": data})
                return data

    data = {"districts": []}
    _DISTRICT_CACHE.update({"loaded_at": now, "data": data})
    return data


async def _redis_client() -> Redis:
    global _REDIS
    if _REDIS is None:
        _REDIS = Redis.from_url(REDIS_URL, decode_responses=True)
    return _REDIS


async def _load_cached_dashboard() -> Optional[Dict[str, Any]]:
    try:
        r = await _redis_client()
        raw = await r.get(METRICS_DASHBOARD_KEY)
        if not raw:
            return None
        return json.loads(raw)
    except Exception:
        return None


async def _load_crewai_roles() -> Dict[str, int]:
    now = time.time()
    if now - _CREWAI_CACHE.get("loaded_at", 0.0) < REGISTRY_CACHE_TTL:
        return _CREWAI_CACHE.get("data", {})

    out: Dict[str, int] = {}
    try:
        async with httpx.AsyncClient(timeout=8.0) as client:
            resp = await client.get(f"{CREWAI_SERVICE_URL}/crew/agents")
            if resp.status_code == 200:
                payload = resp.json()
                for aid, info in payload.items():
                    default_roles = info.get("default_roles")
                    out[str(aid)] = int(default_roles) if isinstance(default_roles, int) else 0
    except Exception:
        out = {}

    _CREWAI_CACHE.update({"loaded_at": now, "data": out})
    return out


@router.get("/registry/agents")
async def get_agents() -> Dict[str, Any]:
    reg = _load_registry()
    agents = reg.get("agents", {}) if isinstance(reg, dict) else {}
    role_counts = await _load_crewai_roles()

    items: List[Dict[str, Any]] = []
    for agent_id, cfg in agents.items():
        if not isinstance(cfg, dict):
            continue
        domains = cfg.get("domains") or []
        district_id = cfg.get("district_id") or "city-core"
        items.append(
            {
                "agent_id": agent_id,
                "title": cfg.get("display_name") or agent_id,
                "role": cfg.get("canonical_role") or "",
                "domain_primary": domains[0] if domains else "general",
                "domain_aliases": domains[1:] if len(domains) > 1 else [],
                "visibility": cfg.get("visibility", "public"),
                "status": cfg.get("status", "active"),
                "team": {"subagents_total": role_counts.get(agent_id, 0)},
                "district_id": district_id,
                "avatar_url": cfg.get("avatar_url"),
                "health_url": cfg.get("health_url"),
            }
        )

    return {"items": items, "total": len(items)}


@router.get("/registry/districts")
async def get_districts() -> Dict[str, Any]:
    agents_payload = await get_agents()
    agents = agents_payload.get("items", [])
    by_district: Dict[str, List[Dict[str, Any]]] = {}
    for a in agents:
        by_district.setdefault(a.get("district_id", "city-core"), []).append(a)

    catalog = _load_district_registry().get("districts", [])
    catalog_by_id: Dict[str, Dict[str, Any]] = {
        str(d.get("district_id")): d for d in catalog if isinstance(d, dict) and d.get("district_id")
    }

    district_ids = sorted(set(catalog_by_id.keys()) | set(by_district.keys()))
    items: List[Dict[str, Any]] = []

    for district_id in district_ids:
        members = by_district.get(district_id, [])
        base = catalog_by_id.get(district_id, {})
        domain = base.get("domain") or ("daarion.city" if district_id == "city-core" else f"{district_id}.daarion.city")

        lead_agent_id = base.get("lead_agent_id")
        if not lead_agent_id:
            if district_id == "city-core" and any(m.get("agent_id") == "daarwizz" for m in members):
                lead_agent_id = "daarwizz"
            elif members:
                lead_agent_id = members[0].get("agent_id")
            else:
                lead_agent_id = None

        items.append(
            {
                "district_id": district_id,
                "title": base.get("title") or district_id.replace("-", " ").title(),
                "domain": domain,
                "status": base.get("status", "active"),
                "logo_url": base.get("logo_url"),
                "health_url": base.get("health_url"),
                "well_known": {
                    "manifest": f"https://{domain}/.well-known/daarion-district.json",
                    "health": f"https://{domain}/.well-known/daarion-health.json",
                    "capabilities": f"https://{domain}/.well-known/daarion-capabilities.json",
                },
                "lead_agent_id": lead_agent_id,
                "agents_total": len(members),
            }
        )

    return {"items": items, "total": len(items)}


@router.get("/metrics")
async def get_metrics() -> Dict[str, Any]:
    agents_payload = await get_agents()
    districts_payload = await get_districts()
    agents = agents_payload.get("items", [])

    memory_vectors = 0
    try:
        async with httpx.AsyncClient(timeout=5.0) as client:
            resp = await client.get(f"{MEMORY_SERVICE_URL}/health")
            if resp.status_code == 200:
                data = resp.json()
                memory_vectors = int(
                    data.get("vector_store", {})
                    .get("memories", {})
                    .get("vectors_count", 0)
                )
    except Exception:
        memory_vectors = 0

    return {
        "nodes": 1,
        "districts": districts_payload.get("total", 0),
        "agents": len(agents),
        "subagents": sum(int((a.get("team") or {}).get("subagents_total", 0)) for a in agents),
        "memory_vectors": memory_vectors,
    }


@router.get("/metrics/dashboard")
async def get_metrics_dashboard() -> Dict[str, Any]:
    cached = await _load_cached_dashboard()
    if cached:
        return cached

    metrics = await get_metrics()
    districts_payload = await get_districts()
    districts = districts_payload.get("items", [])

    by_district = []
    for d in districts:
        by_district.append(
            {
                "district_id": d.get("district_id"),
                "title": d.get("title"),
                "domain": d.get("domain"),
                "status": d.get("status"),
                "ok": None,
                "agents_total": d.get("agents_total", 0),
                "agents_online": None,
                "latency_ms": None,
                "last_check_ts": None,
            }
        )

    return {
        "global": {
            "nodes": metrics.get("nodes", 1),
            "districts": metrics.get("districts", 0),
            "agents": metrics.get("agents", 0),
            "subagents": metrics.get("subagents", 0),
            "memory_vectors": metrics.get("memory_vectors", 0),
            "districts_online": 0,
            "agents_online": 0,
        },
        "by_district": by_district,
        "updated_at": _now_iso(),
        "source": "fallback_registry",
    }
