"""Tests for Phase 83: Grid Integration."""

import json
import pytest
from pathlib import Path

import brynq.grid_integration as gi


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    """Redirect storage to tmp_path."""
    grid_dir = tmp_path / "grid"
    leases_dir = grid_dir / "leases"
    leases_dir.mkdir(parents=True, exist_ok=True)

    monkeypatch.setattr(gi, "GRID_DIR", grid_dir)
    monkeypatch.setattr(gi, "LEASES_DIR", leases_dir)
    monkeypatch.setattr(gi, "TASKS_FILE", grid_dir / "tasks.json")
    monkeypatch.setattr(gi, "METRICS_FILE", grid_dir / "metrics.jsonl")
    gi.reset()


# ── submit_to_grid ─────────────────────────────────────────────────────────

class TestSubmitToGrid:
    def test_submit_basic(self):
        task = gi.submit_to_grid("Run tests")
        assert task["description"] == "Run tests"
        assert task["status"] == "pending"
        assert task["task_id"]

    def test_submit_with_capabilities(self):
        task = gi.submit_to_grid("Build", required_capabilities=["docker"])
        assert "docker" in task["required_capabilities"]

    def test_submit_with_priority(self):
        task = gi.submit_to_grid("Urgent", priority="high")
        assert task["priority"] == "high"

    def test_submit_multiple(self):
        gi.submit_to_grid("A")
        gi.submit_to_grid("B")
        gi.submit_to_grid("C")
        status = gi.get_grid_status()
        assert status["total_tasks"] == 3
        assert status["pending_tasks"] == 3

    def test_submit_persists(self, tmp_path):
        gi.submit_to_grid("Persist me")
        f = tmp_path / "grid" / "tasks.json"
        assert f.exists()
        data = json.loads(f.read_text("utf-8"))
        assert len(data["tasks"]) == 1

    def test_submit_logs_metric(self, tmp_path):
        gi.submit_to_grid("Metric test")
        f = tmp_path / "grid" / "metrics.jsonl"
        assert f.exists()
        lines = f.read_text("utf-8").strip().splitlines()
        entry = json.loads(lines[0])
        assert entry["event"] == "task_submitted"


# ── get_grid_status ────────────────────────────────────────────────────────

class TestGridStatus:
    def test_empty_status(self):
        status = gi.get_grid_status()
        assert status["total_tasks"] == 0
        assert status["pending_tasks"] == 0
        assert status["active_leases"] == 0

    def test_status_with_tasks_and_leases(self):
        gi.submit_to_grid("A")
        gi.submit_to_grid("B")
        gi.lease_compute("agent1", ["llm"])
        status = gi.get_grid_status()
        assert status["total_tasks"] == 2
        assert status["active_leases"] == 1

    def test_status_reflects_returned_leases(self):
        lease = gi.lease_compute("agent1", ["llm"])
        gi.return_compute(lease["lease_id"])
        status = gi.get_grid_status()
        assert status["active_leases"] == 0


# ── lease_compute ──────────────────────────────────────────────────────────

class TestLeaseCompute:
    def test_lease_basic(self):
        lease = gi.lease_compute("agent1", ["llm"])
        assert lease["requester_id"] == "agent1"
        assert lease["status"] == "active"
        assert "llm" in lease["capabilities"]

    def test_lease_custom_duration(self):
        lease = gi.lease_compute("agent1", ["llm"], duration_minutes=60)
        assert lease["duration_minutes"] == 60

    def test_lease_multiple(self):
        gi.lease_compute("a1", ["llm"])
        gi.lease_compute("a2", ["code"])
        leases = gi.get_leases()
        assert len(leases) == 2

    def test_lease_persists(self, tmp_path):
        lease = gi.lease_compute("a1", ["x"])
        f = tmp_path / "grid" / "leases" / f"{lease['lease_id']}.json"
        assert f.exists()


# ── return_compute ─────────────────────────────────────────────────────────

class TestReturnCompute:
    def test_return_basic(self):
        lease = gi.lease_compute("a1", ["llm"])
        result = gi.return_compute(lease["lease_id"])
        assert result["ok"] is True
        assert result["lease"]["status"] == "returned"

    def test_return_with_result(self):
        lease = gi.lease_compute("a1", ["llm"])
        result = gi.return_compute(lease["lease_id"], result={"output": "done"})
        assert result["lease"]["result"] == {"output": "done"}

    def test_return_missing_lease(self):
        result = gi.return_compute("nonexistent")
        assert result["ok"] is False

    def test_return_already_returned(self):
        lease = gi.lease_compute("a1", ["llm"])
        gi.return_compute(lease["lease_id"])
        result = gi.return_compute(lease["lease_id"])
        assert result["ok"] is False
        assert "returned" in result["error"]


# ── get_leases ─────────────────────────────────────────────────────────────

class TestGetLeases:
    def test_filter_by_requester(self):
        gi.lease_compute("a1", ["llm"])
        gi.lease_compute("a2", ["code"])
        gi.lease_compute("a1", ["search"])
        assert len(gi.get_leases(requester_id="a1")) == 2
        assert len(gi.get_leases(requester_id="a2")) == 1

    def test_filter_by_status(self):
        l1 = gi.lease_compute("a1", ["llm"])
        gi.lease_compute("a2", ["code"])
        gi.return_compute(l1["lease_id"])
        assert len(gi.get_leases(status="active")) == 1
        assert len(gi.get_leases(status="returned")) == 1

    def test_filter_combined(self):
        l1 = gi.lease_compute("a1", ["llm"])
        gi.lease_compute("a1", ["code"])
        gi.return_compute(l1["lease_id"])
        assert len(gi.get_leases(requester_id="a1", status="returned")) == 1

    def test_empty_leases(self):
        assert gi.get_leases() == []


# ── get_grid_metrics ───────────────────────────────────────────────────────

class TestGridMetrics:
    def test_metrics_basic(self):
        gi.submit_to_grid("A")
        gi.submit_to_grid("B")
        lease = gi.lease_compute("a1", ["llm"])
        gi.return_compute(lease["lease_id"])
        metrics = gi.get_grid_metrics()
        assert metrics["tasks_submitted"] == 2
        assert metrics["leases_created"] == 1
        assert metrics["leases_returned"] == 1

    def test_metrics_utilization(self):
        l1 = gi.lease_compute("a1", ["llm"])
        l2 = gi.lease_compute("a2", ["code"])
        gi.return_compute(l1["lease_id"])
        metrics = gi.get_grid_metrics()
        assert metrics["utilization_pct"] == 50.0

    def test_metrics_empty(self):
        metrics = gi.get_grid_metrics()
        assert metrics["tasks_submitted"] == 0
        assert metrics["leases_created"] == 0

    def test_metrics_window_param(self):
        metrics = gi.get_grid_metrics(hours=1)
        assert metrics["window_hours"] == 1
