"""
Tests for Phase 53: Reusable Task Templates for Common Operations.
"""

import json
import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    """Redirect all template storage to tmp_path."""
    templates_dir = tmp_path / "task_templates"
    templates_dir.mkdir(parents=True, exist_ok=True)

    import brynq.server as srv
    monkeypatch.setattr(srv, "BROKER_DIR", tmp_path)

    import brynq.task_templates as tt
    monkeypatch.setattr(tt, "TEMPLATES_DIR", templates_dir)
    monkeypatch.setattr(tt, "INSTANTIATION_LOG", templates_dir / "instantiation_log.jsonl")


# ── Create Template ──────────────────────────────────────────────────────

class TestCreateTemplate:
    def test_create_basic(self):
        from brynq.task_templates import create_template
        t = create_template("deploy", "Deploy $service", "Deploy $service to $env", ["devops"])
        assert t["name"] == "deploy"
        assert t["title_pattern"] == "Deploy $service"
        assert t["required_skills"] == ["devops"]
        assert t["use_count"] == 0

    def test_create_with_priority(self):
        from brynq.task_templates import create_template
        t = create_template("fix", "Fix $bug", "Fix bug $bug", ["debug"], default_priority="high")
        assert t["default_priority"] == "high"

    def test_create_with_estimated_minutes(self):
        from brynq.task_templates import create_template
        t = create_template("review", "Review $pr", "Review PR $pr", ["code_review"], estimated_minutes=60)
        assert t["estimated_minutes"] == 60

    def test_create_with_category(self):
        from brynq.task_templates import create_template
        t = create_template("test", "Test $module", "Test $module", ["testing"], category="qa")
        assert t["category"] == "qa"

    def test_create_invalid_priority(self):
        from brynq.task_templates import create_template
        with pytest.raises(ValueError, match="Invalid priority"):
            create_template("bad", "Bad", "Bad", [], default_priority="ultra")

    def test_create_duplicate_rejected(self):
        from brynq.task_templates import create_template
        create_template("deploy", "Deploy $s", "Deploy $s", ["devops"])
        with pytest.raises(ValueError, match="already exists"):
            create_template("deploy", "Deploy $s", "Deploy $s", ["devops"])

    def test_create_has_timestamps(self):
        from brynq.task_templates import create_template
        from datetime import datetime
        t = create_template("t", "T", "T", [])
        datetime.fromisoformat(t["created_at"])
        datetime.fromisoformat(t["updated_at"])


# ── Instantiate ──────────────────────────────────────────────────────────

class TestInstantiate:
    def test_instantiate_basic(self):
        from brynq.task_templates import create_template, instantiate
        create_template("deploy", "Deploy $service", "Deploy $service to $env", ["devops"])
        task = instantiate("deploy", {"service": "api", "env": "production"})
        assert task["title"] == "Deploy api"
        assert task["description"] == "Deploy api to production"
        assert task["template_name"] == "deploy"

    def test_instantiate_increments_use_count(self):
        from brynq.task_templates import create_template, instantiate, get_template
        create_template("t", "T $x", "D $x", [])
        instantiate("t", {"x": "1"})
        instantiate("t", {"x": "2"})
        t = get_template("t")
        assert t["use_count"] == 2

    def test_instantiate_no_variables(self):
        from brynq.task_templates import create_template, instantiate
        create_template("simple", "Simple task", "Do the simple thing", ["general"])
        task = instantiate("simple")
        assert task["title"] == "Simple task"

    def test_instantiate_nonexistent_template(self):
        from brynq.task_templates import instantiate
        with pytest.raises(ValueError, match="not found"):
            instantiate("ghost")

    def test_instantiate_returns_task_id(self):
        from brynq.task_templates import create_template, instantiate
        create_template("t", "T", "D", [])
        task = instantiate("t")
        assert "task_id" in task
        assert len(task["task_id"]) > 0

    def test_instantiate_carries_priority(self):
        from brynq.task_templates import create_template, instantiate
        create_template("urgent", "Urgent $x", "D", [], default_priority="critical")
        task = instantiate("urgent", {"x": "fix"})
        assert task["priority"] == "critical"

    def test_instantiate_carries_skills(self):
        from brynq.task_templates import create_template, instantiate
        create_template("t", "T", "D", ["python", "api"])
        task = instantiate("t")
        assert task["required_skills"] == ["python", "api"]


# ── List / Get / Delete ──────────────────────────────────────────────────

class TestListGetDelete:
    def test_list_empty(self):
        from brynq.task_templates import list_templates
        assert list_templates() == []

    def test_list_all(self):
        from brynq.task_templates import create_template, list_templates
        create_template("a", "A", "A", [])
        create_template("b", "B", "B", [])
        templates = list_templates()
        assert len(templates) == 2

    def test_list_by_category(self):
        from brynq.task_templates import create_template, list_templates
        create_template("a", "A", "A", [], category="dev")
        create_template("b", "B", "B", [], category="ops")
        create_template("c", "C", "C", [], category="dev")
        dev = list_templates(category="dev")
        assert len(dev) == 2

    def test_get_existing(self):
        from brynq.task_templates import create_template, get_template
        create_template("x", "X", "X", [])
        t = get_template("x")
        assert t is not None
        assert t["name"] == "x"

    def test_get_nonexistent(self):
        from brynq.task_templates import get_template
        assert get_template("ghost") is None

    def test_delete_existing(self):
        from brynq.task_templates import create_template, delete_template, get_template
        create_template("x", "X", "X", [])
        assert delete_template("x") is True
        assert get_template("x") is None

    def test_delete_nonexistent(self):
        from brynq.task_templates import delete_template
        assert delete_template("ghost") is False


# ── Popular Templates ────────────────────────────────────────────────────

class TestPopularTemplates:
    def test_popular_sorted_by_use_count(self):
        from brynq.task_templates import create_template, instantiate, get_popular_templates
        create_template("a", "A", "A", [])
        create_template("b", "B $x", "B", [])
        instantiate("b", {"x": "1"})
        instantiate("b", {"x": "2"})
        instantiate("b", {"x": "3"})
        instantiate("a")
        popular = get_popular_templates()
        assert popular[0]["name"] == "b"
        assert popular[0]["use_count"] == 3

    def test_popular_respects_limit(self):
        from brynq.task_templates import create_template, get_popular_templates
        for i in range(15):
            create_template(f"t{i}", f"T{i}", f"D{i}", [])
        popular = get_popular_templates(limit=5)
        assert len(popular) == 5


# ── Clone ────────────────────────────────────────────────────────────────

class TestClone:
    def test_clone_basic(self):
        from brynq.task_templates import create_template, clone_template, get_template
        create_template("orig", "O $x", "D $x", ["python"], estimated_minutes=45)
        clone = clone_template("orig", "copy")
        assert clone["name"] == "copy"
        assert clone["title_pattern"] == "O $x"
        assert clone["estimated_minutes"] == 45
        assert clone["use_count"] == 0
        assert get_template("copy") is not None

    def test_clone_nonexistent_source(self):
        from brynq.task_templates import clone_template
        with pytest.raises(ValueError, match="not found"):
            clone_template("ghost", "copy")

    def test_clone_duplicate_target(self):
        from brynq.task_templates import create_template, clone_template
        create_template("a", "A", "A", [])
        create_template("b", "B", "B", [])
        with pytest.raises(ValueError, match="already exists"):
            clone_template("a", "b")
