"""Tests for ``mld doctor`` command."""

import argparse
import json
from pathlib import Path

import pytest

from mld_sdk.doctor_command import CheckResult, cmd_doctor, run_checks


def _make_plugin(tmp_path: Path, *, with_frontend: bool = False, with_tests: bool = True, with_claude: bool = True) -> None:
    """Create a minimal valid plugin project structure."""
    # pyproject.toml
    (tmp_path / "pyproject.toml").write_text("""\
[project]
name = "mld-plugin-test"
version = "0.1.0"
description = "Test plugin"
dependencies = ["mld-sdk>=0.9.0"]

[project.entry-points."mld.plugins"]
test = "mld_plugin_test.plugin:TestPlugin"
""")

    # Source module
    src_dir = tmp_path / "src" / "mld_plugin_test"
    src_dir.mkdir(parents=True)
    (src_dir / "__init__.py").write_text("")
    (src_dir / "plugin.py").write_text("""\
from mld_sdk import AnalysisPlugin

class TestPlugin(AnalysisPlugin):
    PLUGIN_ROUTES_PREFIX = "/test"
""")

    if with_tests:
        tests_dir = tmp_path / "tests"
        tests_dir.mkdir()
        (tests_dir / "test_plugin.py").write_text("def test_placeholder(): pass")

    if with_claude:
        (tmp_path / "CLAUDE.md").write_text("# CLAUDE.md")

    if with_frontend:
        fe_dir = tmp_path / "frontend"
        fe_dir.mkdir()
        (fe_dir / "package.json").write_text(json.dumps({
            "dependencies": {"@morscherlab/mld-sdk": "^0.9.0"},
        }))


class TestRunChecks:

    def test_all_pass_for_valid_plugin(self, tmp_path: Path):
        _make_plugin(tmp_path, with_frontend=True)
        results = run_checks(tmp_path)
        assert all(r.ok for r in results), [r for r in results if not r.ok]

    def test_missing_pyproject(self, tmp_path: Path):
        results = run_checks(tmp_path)
        assert not results[0].ok
        assert "not found" in results[0].detail

    def test_missing_entry_point(self, tmp_path: Path):
        (tmp_path / "pyproject.toml").write_text("[project]\nname = 'test'\n")
        results = run_checks(tmp_path)
        # pyproject ok, entry point fails
        assert results[0].ok
        assert not results[1].ok
        assert "mld.plugins" in results[1].detail

    def test_bad_entry_point_format(self, tmp_path: Path):
        (tmp_path / "pyproject.toml").write_text("""\
[project]
name = "test"

[project.entry-points."mld.plugins"]
bad = "no_colon_here"
""")
        results = run_checks(tmp_path)
        assert not results[1].ok
        assert "colon" in results[1].detail

    def test_missing_source_module(self, tmp_path: Path):
        _make_plugin(tmp_path)
        # Remove source
        (tmp_path / "src" / "mld_plugin_test" / "plugin.py").unlink()
        results = run_checks(tmp_path)
        assert not results[2].ok
        assert "not found" in results[2].detail

    def test_missing_plugin_class(self, tmp_path: Path):
        _make_plugin(tmp_path)
        # Overwrite with no class
        (tmp_path / "src" / "mld_plugin_test" / "plugin.py").write_text("x = 1")
        results = run_checks(tmp_path)
        assert not results[3].ok
        assert "not found" in results[3].detail

    def test_missing_sdk_dependency(self, tmp_path: Path):
        _make_plugin(tmp_path)
        (tmp_path / "pyproject.toml").write_text("""\
[project]
name = "mld-plugin-test"
dependencies = ["fastapi"]

[project.entry-points."mld.plugins"]
test = "mld_plugin_test.plugin:TestPlugin"
""")
        results = run_checks(tmp_path)
        sdk_check = [r for r in results if r.label == "Python SDK dep"][0]
        assert not sdk_check.ok

    def test_missing_frontend_sdk(self, tmp_path: Path):
        _make_plugin(tmp_path, with_frontend=True)
        # Overwrite package.json with no SDK dep
        (tmp_path / "frontend" / "package.json").write_text('{"dependencies":{}}')
        results = run_checks(tmp_path)
        fe_check = [r for r in results if r.label == "Frontend SDK dep"][0]
        assert not fe_check.ok

    def test_no_frontend_is_ok(self, tmp_path: Path):
        _make_plugin(tmp_path, with_frontend=False)
        results = run_checks(tmp_path)
        fe_check = [r for r in results if r.label == "Frontend SDK dep"][0]
        assert fe_check.ok  # "no frontend (ok)"

    def test_missing_tests_dir(self, tmp_path: Path):
        _make_plugin(tmp_path, with_tests=False)
        results = run_checks(tmp_path)
        tests_check = [r for r in results if r.label == "Tests"][0]
        assert not tests_check.ok

    def test_missing_ai_instructions(self, tmp_path: Path):
        _make_plugin(tmp_path, with_claude=False)
        results = run_checks(tmp_path)
        ai_check = [r for r in results if r.label == "AI instructions"][0]
        assert not ai_check.ok

    def test_agents_md_satisfies_ai_check(self, tmp_path: Path):
        _make_plugin(tmp_path, with_claude=False)
        (tmp_path / "AGENTS.md").write_text("# AGENTS.md")
        results = run_checks(tmp_path)
        ai_check = [r for r in results if r.label == "AI instructions"][0]
        assert ai_check.ok
        assert "AGENTS.md" in ai_check.detail


class TestCmdDoctor:

    def test_exits_zero_on_success(self, tmp_path: Path):
        _make_plugin(tmp_path)
        args = argparse.Namespace(path=str(tmp_path))
        cmd_doctor(args)  # should not raise

    def test_exits_one_on_failure(self, tmp_path: Path):
        args = argparse.Namespace(path=str(tmp_path))
        with pytest.raises(SystemExit) as exc_info:
            cmd_doctor(args)
        assert exc_info.value.code == 1


class TestCLIIntegration:

    def test_doctor_subcommand_registered(self):
        from mld_sdk.cli import main
        with pytest.raises(SystemExit) as exc_info:
            main(["doctor", "--help"])
        assert exc_info.value.code == 0
