"""
Phase 83: Grid Integration — Wire Swarm V2 into the compute grid (Phase 56).

Provides a high-level interface for submitting tasks to the compute grid,
leasing compute resources, and tracking grid health and metrics.

Storage:
  state/broker/grid/
    leases/              — one JSON file per lease
    tasks.json           — submitted grid tasks
    metrics.jsonl        — grid-level metrics log
"""

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

from brynq.server import BROKER_DIR, _write_json_sync, _read_jsonl_sync, _append_jsonl_sync

logger = logging.getLogger(__name__)

# ── Paths ──────────────────────────────────────────────────────────────────

GRID_DIR = BROKER_DIR / "grid"
LEASES_DIR = GRID_DIR / "leases"
TASKS_FILE = GRID_DIR / "tasks.json"
METRICS_FILE = GRID_DIR / "metrics.jsonl"

for _d in (GRID_DIR, LEASES_DIR):
    _d.mkdir(parents=True, exist_ok=True)


def reset() -> None:
    """Reset. Used by tests."""
    pass


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


def _load_tasks() -> dict:
    if TASKS_FILE.exists():
        try:
            return json.loads(TASKS_FILE.read_text("utf-8"))
        except (json.JSONDecodeError, OSError):
            return {"tasks": []}
    return {"tasks": []}


def _save_tasks(data: dict) -> None:
    _write_json_sync(TASKS_FILE, data)


def _lease_file(lease_id: str) -> Path:
    return LEASES_DIR / f"{lease_id}.json"


def _load_lease(lease_id: str) -> Optional[dict]:
    f = _lease_file(lease_id)
    if not f.exists():
        return None
    try:
        return json.loads(f.read_text("utf-8"))
    except (json.JSONDecodeError, OSError):
        return None


def _save_lease(lease: dict) -> None:
    _write_json_sync(_lease_file(lease["lease_id"]), lease)


# ── Public API ─────────────────────────────────────────────────────────────

def submit_to_grid(
    task_description: str,
    required_capabilities: Optional[List[str]] = None,
    priority: str = "normal",
) -> dict:
    """Submit a task to the compute grid."""
    task_id = uuid.uuid4().hex[:12]
    task = {
        "task_id": task_id,
        "description": task_description,
        "required_capabilities": required_capabilities or [],
        "priority": priority,
        "status": "pending",
        "submitted_at": _now(),
        "completed_at": None,
        "result": None,
    }
    data = _load_tasks()
    data["tasks"].append(task)
    _save_tasks(data)

    _append_jsonl_sync(METRICS_FILE, {
        "event": "task_submitted",
        "task_id": task_id,
        "priority": priority,
        "ts": _now(),
    })
    logger.info("Submitted grid task %s: %s", task_id, task_description[:60])
    return task


def get_grid_status() -> dict:
    """Current grid health: active leases, pending tasks, throughput."""
    data = _load_tasks()
    tasks = data.get("tasks", [])
    pending = [t for t in tasks if t.get("status") == "pending"]
    completed = [t for t in tasks if t.get("status") == "completed"]

    leases = get_leases()
    active_leases = [l for l in leases if l.get("status") == "active"]

    return {
        "total_tasks": len(tasks),
        "pending_tasks": len(pending),
        "completed_tasks": len(completed),
        "active_leases": len(active_leases),
        "total_leases": len(leases),
    }


def lease_compute(
    requester_id: str,
    capabilities: List[str],
    duration_minutes: int = 30,
) -> dict:
    """Lease compute from available grid nodes."""
    lease_id = uuid.uuid4().hex[:12]
    lease = {
        "lease_id": lease_id,
        "requester_id": requester_id,
        "capabilities": capabilities,
        "duration_minutes": duration_minutes,
        "status": "active",
        "leased_at": _now(),
        "expires_at": None,
        "returned_at": None,
        "result": None,
    }
    _save_lease(lease)
    _append_jsonl_sync(METRICS_FILE, {
        "event": "lease_created",
        "lease_id": lease_id,
        "requester_id": requester_id,
        "ts": _now(),
    })
    logger.info("Leased compute %s for %s (%d min)", lease_id, requester_id, duration_minutes)
    return lease


def return_compute(lease_id: str, result: Any = None) -> dict:
    """Return leased compute with optional result."""
    lease = _load_lease(lease_id)
    if not lease:
        return {"ok": False, "error": f"Lease '{lease_id}' not found"}
    if lease["status"] != "active":
        return {"ok": False, "error": f"Lease '{lease_id}' is {lease['status']}"}

    lease["status"] = "returned"
    lease["returned_at"] = _now()
    lease["result"] = result
    _save_lease(lease)
    _append_jsonl_sync(METRICS_FILE, {
        "event": "lease_returned",
        "lease_id": lease_id,
        "ts": _now(),
    })
    return {"ok": True, "lease": lease}


def get_leases(
    requester_id: Optional[str] = None,
    status: Optional[str] = None,
) -> List[dict]:
    """List active/completed leases."""
    leases = []
    if not LEASES_DIR.exists():
        return leases
    for f in sorted(LEASES_DIR.glob("*.json")):
        try:
            lease = json.loads(f.read_text("utf-8"))
            if requester_id and lease.get("requester_id") != requester_id:
                continue
            if status and lease.get("status") != status:
                continue
            leases.append(lease)
        except (json.JSONDecodeError, OSError):
            continue
    return leases


def get_grid_metrics(hours: int = 24) -> dict:
    """Jobs completed, avg time, utilization %."""
    entries = _read_jsonl_sync(METRICS_FILE)
    submitted = [e for e in entries if e.get("event") == "task_submitted"]
    leases_created = [e for e in entries if e.get("event") == "lease_created"]
    leases_returned = [e for e in entries if e.get("event") == "lease_returned"]

    data = _load_tasks()
    tasks = data.get("tasks", [])
    completed = [t for t in tasks if t.get("status") == "completed"]

    return {
        "window_hours": hours,
        "tasks_submitted": len(submitted),
        "tasks_completed": len(completed),
        "leases_created": len(leases_created),
        "leases_returned": len(leases_returned),
        "utilization_pct": round(
            len(leases_returned) / max(len(leases_created), 1) * 100, 1
        ),
    }
