"""Tests for agentmux.core.templates — template rendering and the TEMPLATES registry."""

import json
from pathlib import Path

import pytest

from agentmux.core.component import (
    Agent,
    Command,
    ComponentType,
    Instructions,
    MCPConfig,
    Rule,
    Skill,
)
from agentmux.core.templates import TEMPLATES, ComponentTemplate, render_mcp, render_template


# ---------------------------------------------------------------------------
# TEMPLATES registry coverage
# ---------------------------------------------------------------------------


def test_templates_covers_all_component_types() -> None:
    """Every ComponentType must have a corresponding entry in TEMPLATES."""
    for comp_type in ComponentType:
        assert comp_type in TEMPLATES, f"TEMPLATES is missing entry for {comp_type.value!r}"


def test_all_templates_are_component_template_instances() -> None:
    for comp_type, tmpl in TEMPLATES.items():
        assert isinstance(tmpl, ComponentTemplate), (
            f"TEMPLATES[{comp_type.value!r}] is not a ComponentTemplate"
        )


# ---------------------------------------------------------------------------
# Agent template
# ---------------------------------------------------------------------------


def test_agent_template_renders_name_and_description(tmp_path: Path) -> None:
    content = render_template(ComponentType.AGENT, name="my-agent", description="Does things")
    assert "name: my-agent" in content
    assert "description: Does things" in content


def test_agent_template_omits_description_when_empty() -> None:
    """Rendering without a description must not emit an empty YAML key."""
    content = render_template(ComponentType.AGENT, name="my-agent")
    assert "name: my-agent" in content
    assert "description:" not in content


def test_agent_template_is_parseable_by_agent_model(tmp_path: Path) -> None:
    content = render_template(ComponentType.AGENT, name="my-agent", description="Test agent")
    agent_file = tmp_path / "my-agent.md"
    agent_file.write_text(content)
    agent = Agent.from_file(agent_file)
    assert agent.name == "my-agent"
    assert agent.description == "Test agent"


def test_agent_template_is_parseable_without_description(tmp_path: Path) -> None:
    content = render_template(ComponentType.AGENT, name="no-desc-agent")
    agent_file = tmp_path / "no-desc-agent.md"
    agent_file.write_text(content)
    agent = Agent.from_file(agent_file)
    assert agent.name == "no-desc-agent"
    assert agent.description == ""


def test_agent_template_output_filename() -> None:
    tmpl = TEMPLATES[ComponentType.AGENT]
    assert tmpl.output_filename("my-agent") == "my-agent.md"
    assert tmpl.creates_directory is False


# ---------------------------------------------------------------------------
# Command template
# ---------------------------------------------------------------------------


def test_command_template_renders_name(tmp_path: Path) -> None:
    content = render_template(ComponentType.COMMAND, name="fix-tests")
    assert "name: fix-tests" in content


def test_command_template_is_parseable(tmp_path: Path) -> None:
    content = render_template(ComponentType.COMMAND, name="my-command", description="Run stuff")
    cmd_file = tmp_path / "my-command.md"
    cmd_file.write_text(content)
    cmd = Command.from_file(cmd_file)
    assert cmd.name == "my-command"
    assert cmd.description == "Run stuff"


def test_command_template_output_filename() -> None:
    tmpl = TEMPLATES[ComponentType.COMMAND]
    assert tmpl.output_filename("do-thing") == "do-thing.md"


# ---------------------------------------------------------------------------
# Skill template
# ---------------------------------------------------------------------------


def test_skill_template_creates_directory_flag() -> None:
    tmpl = TEMPLATES[ComponentType.SKILL]
    assert tmpl.creates_directory is True


def test_skill_template_output_filename_is_directory_name() -> None:
    tmpl = TEMPLATES[ComponentType.SKILL]
    assert tmpl.output_filename("my-skill") == "my-skill"


def test_skill_template_is_parseable(tmp_path: Path) -> None:
    content = render_template(ComponentType.SKILL, name="my-skill", description="A great skill")
    skill_dir = tmp_path / "my-skill"
    skill_dir.mkdir()
    (skill_dir / "SKILL.md").write_text(content)
    skill = Skill.from_dir(skill_dir)
    assert skill.name == "my-skill"
    assert skill.description == "A great skill"


def test_skill_template_is_parseable_without_description(tmp_path: Path) -> None:
    content = render_template(ComponentType.SKILL, name="bare-skill")
    skill_dir = tmp_path / "bare-skill"
    skill_dir.mkdir()
    (skill_dir / "SKILL.md").write_text(content)
    skill = Skill.from_dir(skill_dir)
    assert skill.name == "bare-skill"
    assert skill.description == ""


# ---------------------------------------------------------------------------
# Rule template
# ---------------------------------------------------------------------------


def test_rule_template_renders_name(tmp_path: Path) -> None:
    content = render_template(ComponentType.RULE, name="no-console-log")
    assert "name: no-console-log" in content
    assert "alwaysApply: false" in content


def test_rule_template_is_parseable(tmp_path: Path) -> None:
    content = render_template(ComponentType.RULE, name="my-rule", description="A rule")
    rule_file = tmp_path / "my-rule.md"
    rule_file.write_text(content)
    rule = Rule.from_file(rule_file)
    assert rule.name == "my-rule"
    assert rule.always_apply is False


def test_rule_template_output_filename() -> None:
    tmpl = TEMPLATES[ComponentType.RULE]
    assert tmpl.output_filename("my-rule") == "my-rule.md"


# ---------------------------------------------------------------------------
# Instructions template
# ---------------------------------------------------------------------------


def test_instructions_template_renders_body(tmp_path: Path) -> None:
    content = render_template(ComponentType.INSTRUCTIONS, name="instructions")
    assert len(content.strip()) > 0


def test_instructions_template_is_parseable(tmp_path: Path) -> None:
    content = render_template(ComponentType.INSTRUCTIONS, name="instructions")
    instr_file = tmp_path / "instructions.md"
    instr_file.write_text(content)
    instr = Instructions.from_file(instr_file)
    assert len(instr.body) > 0


def test_instructions_template_output_filename() -> None:
    tmpl = TEMPLATES[ComponentType.INSTRUCTIONS]
    assert tmpl.output_filename("instructions") == "instructions.md"


# ---------------------------------------------------------------------------
# MCP template
# ---------------------------------------------------------------------------


def test_mcp_template_render_raises_not_implemented() -> None:
    tmpl = TEMPLATES[ComponentType.MCP]
    with pytest.raises(NotImplementedError):
        tmpl.render(name="my-mcp")


def test_render_mcp_produces_valid_json() -> None:
    content = render_mcp()
    data = json.loads(content)
    assert "mcpServers" in data
    assert isinstance(data["mcpServers"], dict)


def test_mcp_template_is_parseable(tmp_path: Path) -> None:
    content = render_template(ComponentType.MCP, name="my-mcp")
    mcp_file = tmp_path / "mcp.json"
    mcp_file.write_text(content)
    mcp = MCPConfig.from_file(mcp_file)
    assert isinstance(mcp.servers, dict)
    assert len(mcp.servers) == 0


def test_mcp_template_output_filename() -> None:
    tmpl = TEMPLATES[ComponentType.MCP]
    assert tmpl.output_filename("anything") == "mcp.json"


def test_render_template_dispatches_to_render_mcp() -> None:
    """render_template(MCP, ...) should return the same JSON as render_mcp()."""
    assert render_template(ComponentType.MCP, name="ignored") == render_mcp()
