from __future__ import annotations

import importlib.util
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[1]
SCRIPT_PATH = REPO_ROOT / "scripts" / "init_python_cli_template.py"
SRC_DIR = REPO_ROOT / "src"


def load_module():
    if str(SRC_DIR) not in sys.path:
        sys.path.insert(0, str(SRC_DIR))
    spec = importlib.util.spec_from_file_location("mcp_template_cli.cli", SRC_DIR / "mcp_template_cli" / "cli.py")
    module = importlib.util.module_from_spec(spec)
    assert spec.loader is not None
    spec.loader.exec_module(module)
    return module


class InitPythonCliTemplateTests(unittest.TestCase):
    def run_cli(self, *args: str, **kwargs: object) -> subprocess.CompletedProcess[str]:
        env = dict(kwargs.pop("env", {}))
        env["PYTHONPATH"] = str(SRC_DIR)
        return subprocess.run([sys.executable, "-m", "mcp_template_cli", *args], env=env, **kwargs)

    def test_slug_helpers_reject_invalid_values(self):
        module = load_module()

        self.assertEqual(module._server_slug("Demo Server"), "demo-server")
        self.assertEqual(module._tool_slug("Run Profile"), "run_profile")
        self.assertEqual(module._env_prefix("demo-server"), "DEMO_SERVER")
        self.assertEqual(module._env_prefix("FACTOR_TOOLS"), "FACTOR_TOOLS")

        with self.assertRaises(ValueError):
            module._server_slug("!!!")

        with self.assertRaises(ValueError):
            module._tool_slug("123-run")

        with self.assertRaises(ValueError):
            module._env_prefix("!!!")

        with self.assertRaises(ValueError):
            module._env_prefix("123_TOOLS")

    def test_copy_template_ignores_python_cache_files(self):
        module = load_module()

        with tempfile.TemporaryDirectory() as tmp_dir:
            tmp_path = Path(tmp_dir)
            template_dir = tmp_path / "template"
            template_dir.mkdir()
            (template_dir / "server.py").write_text("print('ok')\n", encoding="utf-8")
            (template_dir / "__pycache__").mkdir()
            (template_dir / "__pycache__" / "server.pyc").write_bytes(b"compiled")
            (template_dir / "ignored.pyc").write_bytes(b"compiled")
            (template_dir / ".DS_Store").write_text("noise", encoding="utf-8")

            module.TEMPLATE_DIR = template_dir
            dest = tmp_path / "out"
            module._copy_template(dest)

            self.assertTrue((dest / "server.py").exists())
            self.assertFalse((dest / "__pycache__").exists())
            self.assertFalse((dest / "ignored.pyc").exists())
            self.assertFalse((dest / ".DS_Store").exists())

    def test_packaged_assets_exist(self):
        module = load_module()

        self.assertTrue((module.BUNDLED_DATA_DIR / "python-cli-template" / "server.py").exists())
        self.assertTrue((module.BUNDLED_DATA_DIR / "python-cli-template" / "doctor.py").exists())
        self.assertTrue((module.BUNDLED_DATA_DIR / "examples" / "claude_desktop_config.sample.json").exists())

    def test_bootstrap_script_rewrites_template_files(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"
            result = self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                "--binary-name",
                "demo-cli",
                capture_output=True,
                text=True,
                check=True,
            )

            server_text = (dest / "mcp_server.py").read_text(encoding="utf-8")
            doctor_text = (dest / "support" / "doctor.py").read_text(encoding="utf-8")
            dockerfile_text = (dest / "support" / "Dockerfile").read_text(encoding="utf-8")
            gitignore_text = (dest / ".gitignore").read_text(encoding="utf-8")
            makefile_text = (dest / "support" / "Makefile").read_text(encoding="utf-8")
            requirements_text = (dest / "support" / "requirements.txt").read_text(encoding="utf-8")
            sample_input_text = (dest / "support" / "sample-input.txt").read_text(encoding="utf-8")
            sample_output_readme = (dest / "support" / "sample-output" / "README.txt").read_text(encoding="utf-8")
            env_example_text = (dest / ".env.example").read_text(encoding="utf-8")
            start_here_text = (dest / "START_HERE.md").read_text(encoding="utf-8")
            claude_desktop_text = (dest / "examples" / "claude_desktop_config.sample.json").read_text(
                encoding="utf-8"
            )
            claude_code_text = (dest / "examples" / "claude_code_mcp.sample.json").read_text(
                encoding="utf-8"
            )

            self.assertIn("Created template at", result.stdout)
            self.assertIn("server name: demo-server", result.stdout)
            self.assertIn("tool names: run_profile", result.stdout)
            self.assertIn("bin path: /ABS/PATH/TO/demo-cli", result.stdout)
            self.assertIn(f"python path: {sys.executable}", result.stdout)
            self.assertIn("read dirs: /ABS/PATH/TO/data,/ABS/PATH/TO/profiles,/ABS/PATH/TO/artifacts", result.stdout)
            self.assertIn("write dirs: /ABS/PATH/TO/artifacts", result.stdout)
            self.assertIn('FastMCP("demo-server")', server_text)
            self.assertIn("def version()", server_text)
            self.assertIn("def paths()", server_text)
            self.assertIn('TEMPLATE_VERSION = "2026.03.12"', server_text)
            self.assertIn("def run_profile(", server_text)
            self.assertIn("DEMO_SERVER_ALLOWED_READ_DIRS", server_text)
            self.assertIn("DEMO_SERVER_ALLOWED_WRITE_DIRS", server_text)
            self.assertIn("DEMO_SERVER_CMD_TIMEOUT_SEC", server_text)
            self.assertIn("DEMO_SERVER_BIN", server_text)
            self.assertIn("demo-cli", server_text)
            self.assertNotIn("project-template", server_text)
            self.assertNotIn("run_example", server_text)
            self.assertNotIn("PROJECT_BIN", doctor_text)
            self.assertIn("DEMO_SERVER_BIN", doctor_text)
            self.assertIn("demo-cli", dockerfile_text)
            self.assertIn('"demo-server"', claude_desktop_text)
            self.assertIn('"DEMO_SERVER_BIN"', claude_desktop_text)
            self.assertIn('"demo-server"', claude_code_text)
            self.assertIn('"DEMO_SERVER_ALLOWED_READ_DIRS"', claude_code_text)
            self.assertIn(str(dest / "mcp_server.py"), claude_desktop_text)
            self.assertIn(sys.executable, claude_desktop_text)
            self.assertIn("DEMO_SERVER_BIN=/ABS/PATH/TO/demo-cli", env_example_text)
            self.assertIn("DEMO_SERVER_ALLOWED_READ_DIRS=", env_example_text)
            self.assertIn("DEMO_SERVER_ALLOWED_WRITE_DIRS=", env_example_text)
            self.assertIn("DEMO_SERVER_CMD_TIMEOUT_SEC=180", env_example_text)
            self.assertIn("__pycache__/", gitignore_text)
            self.assertIn(".venv/", gitignore_text)
            self.assertIn(".env", gitignore_text)
            self.assertIn("install:", makefile_text)
            self.assertIn("doctor:", makefile_text)
            self.assertIn("run:", makefile_text)
            self.assertIn("support/requirements.txt", makefile_text)
            self.assertIn("support/doctor.py --server mcp_server.py", makefile_text)
            self.assertIn('CMD ["python", "/app/mcp_server.py"]', dockerfile_text)
            self.assertIn("COPY mcp_server.py /app/mcp_server.py", dockerfile_text)
            self.assertIn("COPY support/doctor.py /app/doctor.py", dockerfile_text)
            self.assertIn("COPY support/bin/demo-cli /usr/local/bin/demo-cli", dockerfile_text)
            self.assertEqual(requirements_text, "mcp\n")
            self.assertIn("Replace this file with a real input", sample_input_text)
            self.assertIn("local wrapper output", sample_output_readme)
            self.assertIn("# demo-server MCP Wrapper", start_here_text)
            self.assertIn("mcp_server.py", start_here_text)
            self.assertIn("run_profile", start_here_text)
            self.assertIn("DEMO_SERVER_BIN", start_here_text)
            self.assertIn("healthcheck", start_here_text)
            self.assertTrue((dest / "support" / "bin" / "demo-cli").exists())
            self.assertFalse((dest / "support" / "bin" / "project-cli").exists())

    def test_bootstrap_script_allows_env_prefix_override(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                "--binary-name",
                "demo-cli",
                "--env-prefix",
                "CUSTOM_TOOLS",
                capture_output=True,
                text=True,
                check=True,
            )

            server_text = (dest / "mcp_server.py").read_text(encoding="utf-8")
            env_example_text = (dest / ".env.example").read_text(encoding="utf-8")

            self.assertIn("CUSTOM_TOOLS_BIN", server_text)
            self.assertIn("CUSTOM_TOOLS_ALLOWED_READ_DIRS", server_text)
            self.assertIn("CUSTOM_TOOLS_BIN=/ABS/PATH/TO/demo-cli", env_example_text)

    def test_bootstrap_script_prefills_bin_path(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                "--binary-name",
                "demo-cli",
                "--bin-path",
                "/usr/local/bin/demo-cli",
                capture_output=True,
                text=True,
                check=True,
            )

            env_example_text = (dest / ".env.example").read_text(encoding="utf-8")
            claude_desktop_text = (dest / "examples" / "claude_desktop_config.sample.json").read_text(
                encoding="utf-8"
            )

            self.assertIn("DEMO_SERVER_BIN=/usr/local/bin/demo-cli", env_example_text)
            self.assertIn('"/usr/local/bin/demo-cli"', claude_desktop_text)

    def test_bootstrap_script_prefills_allowed_dirs(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                "--read-dirs",
                "/data,/profiles",
                "--write-dirs",
                "/artifacts",
                capture_output=True,
                text=True,
                check=True,
            )

            env_example_text = (dest / ".env.example").read_text(encoding="utf-8")
            claude_desktop_text = (dest / "examples" / "claude_desktop_config.sample.json").read_text(
                encoding="utf-8"
            )

            self.assertIn("DEMO_SERVER_ALLOWED_READ_DIRS=/data,/profiles", env_example_text)
            self.assertIn("DEMO_SERVER_ALLOWED_WRITE_DIRS=/artifacts", env_example_text)
            self.assertIn('"/data,/profiles"', claude_desktop_text)
            self.assertIn('"/artifacts"', claude_desktop_text)

    def test_bootstrap_script_prefills_python_path(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                "--python-path",
                "/opt/custom/python",
                capture_output=True,
                text=True,
                check=True,
            )

            claude_desktop_text = (dest / "examples" / "claude_desktop_config.sample.json").read_text(
                encoding="utf-8"
            )

            self.assertIn('"/opt/custom/python"', claude_desktop_text)

    def test_bootstrap_script_supports_multiple_tool_names(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"
            result = self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "analyze_csv",
                "--tool-name",
                "analyze_compare",
                capture_output=True,
                text=True,
                check=True,
            )

            server_text = (dest / "mcp_server.py").read_text(encoding="utf-8")
            start_here_text = (dest / "START_HERE.md").read_text(encoding="utf-8")

            self.assertIn("tool names: analyze_csv, analyze_compare", result.stdout)
            self.assertIn("def analyze_csv(", server_text)
            self.assertIn('cmd = ["analyze-csv"', server_text)
            self.assertIn("def analyze_compare(", server_text)
            self.assertIn('cmd = ["analyze-compare"', server_text)
            self.assertNotIn("def run_example(", server_text)
            self.assertIn("`analyze_csv`", start_here_text)
            self.assertIn("`analyze_compare`", start_here_text)

    def test_bootstrap_script_supports_tool_subcommand_overrides(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "analyze_compare",
                "--tool-subcommand",
                "analyze_compare=analyze-compare",
                capture_output=True,
                text=True,
                check=True,
            )

            server_text = (dest / "mcp_server.py").read_text(encoding="utf-8")
            start_here_text = (dest / "START_HERE.md").read_text(encoding="utf-8")

            self.assertIn('cmd = ["analyze-compare"', server_text)
            self.assertIn("`analyze_compare` -> `analyze-compare`", start_here_text)

    def test_bootstrap_script_can_infer_tool_shape_from_help(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            tmp_path = Path(tmp_dir)
            fake_bin = tmp_path / "fake-cli"
            fake_bin.write_text(
                "\n".join(
                    [
                        "#!/usr/bin/env python3",
                        "import sys",
                        "cmd = sys.argv[1:]",
                        "if cmd == ['explain-analyze', '--help']:",
                        "    print('Usage: fake-cli explain-analyze [OPTIONS] --model <MODEL> --analysis-json <ANALYSIS_JSON> --question <QUESTION>')",
                        "    print()",
                        "    print('Options:')",
                        "    print('      --backend <BACKEND>              [default: local] [possible values: local, bedrock]')",
                        "    print('      --model <MODEL>')",
                        "    print('      --analysis-json <ANALYSIS_JSON>')",
                        "    print('      --question <QUESTION>')",
                        "    print('  -h, --help                           Print help')",
                        "    raise SystemExit(0)",
                        "print('unexpected', cmd)",
                        "raise SystemExit(1)",
                    ]
                )
                + "\n",
                encoding="utf-8",
            )
            fake_bin.chmod(0o755)

            dest = tmp_path / "generated-template"
            self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "explain_analyze",
                "--tool-subcommand",
                "explain_analyze=explain-analyze",
                "--binary-name",
                "fake-cli",
                "--bin-path",
                str(fake_bin),
                "--infer-tools-from-help",
                capture_output=True,
                text=True,
                check=True,
            )

            server_text = (dest / "mcp_server.py").read_text(encoding="utf-8")

            self.assertIn("def explain_analyze(", server_text)
            self.assertIn("    model: str,", server_text)
            self.assertIn("analysis_json: str,", server_text)
            self.assertIn("question: str,", server_text)
            self.assertIn('backend: str = "local",', server_text)
            self.assertNotIn('def explain_analyze(\n    backend: str = "local",\n    model: str,', server_text)
            self.assertIn('if backend is not None and backend not in {\'local\', \'bedrock\'}:', server_text)
            self.assertIn('analysis_json_path = _validate_read_path(analysis_json, "analysis_json")', server_text)
            self.assertIn('cmd = ["explain-analyze"]', server_text)
            self.assertIn('cmd.extend(["--analysis-json", str(analysis_json_path)])', server_text)

    def test_bootstrap_script_infers_factorlens_like_flags_without_path_or_int_regressions(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            tmp_path = Path(tmp_dir)
            fake_bin = tmp_path / "fake-cli"
            fake_bin.write_text(
                "\n".join(
                    [
                        "#!/usr/bin/env python3",
                        "import sys",
                        "cmd = sys.argv[1:]",
                        "if cmd == ['analyze-compare', '--help']:",
                        "    print('Usage: fake-cli analyze-compare [OPTIONS] --base <BASE> --new <NEW>')",
                        "    print()",
                        "    print('Options:')",
                        "    print('      --base <BASE>')",
                        "    print('      --new <NEW>')",
                        "    print('      --top-movers <TOP_MOVERS>        [default: 10]')",
                        "    print('      --output-format <OUTPUT_FORMAT>  [default: both] [possible values: md, html, json, both]')",
                        "    print('      --out <OUT>')",
                        "    print('  -h, --help                           Print help')",
                        "    raise SystemExit(0)",
                        "print('unexpected', cmd)",
                        "raise SystemExit(1)",
                    ]
                )
                + "\n",
                encoding="utf-8",
            )
            fake_bin.chmod(0o755)

            dest = tmp_path / "generated-template"
            self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "analyze_compare",
                "--tool-subcommand",
                "analyze_compare=analyze-compare",
                "--binary-name",
                "fake-cli",
                "--bin-path",
                str(fake_bin),
                "--infer-tools-from-help",
                capture_output=True,
                text=True,
                check=True,
            )

            server_path = dest / "mcp_server.py"
            server_text = server_path.read_text(encoding="utf-8")

            compile(server_text, str(server_path), "exec")
            self.assertIn("top_movers: int = 10,", server_text)
            self.assertIn('cmd.extend(["--top-movers", str(top_movers)])', server_text)
            self.assertNotIn("output_format_path", server_text)
            self.assertIn('cmd.extend(["--output-format", output_format])', server_text)
            self.assertIn('out_path = _validate_write_path(out, "out")', server_text)

    def test_help_inference_times_out_cleanly(self):
        module = load_module()

        with tempfile.TemporaryDirectory() as tmp_dir:
            tmp_path = Path(tmp_dir)
            fake_bin = tmp_path / "slow-cli"
            fake_bin.write_text(
                "\n".join(
                    [
                        "#!/usr/bin/env python3",
                        "import time",
                        "time.sleep(1)",
                    ]
                )
                + "\n",
                encoding="utf-8",
            )
            fake_bin.chmod(0o755)

            with self.assertRaisesRegex(ValueError, "timed out reading help"):
                module._tool_help_text(str(fake_bin), "slow", timeout_sec=0.01)

    def test_bootstrap_script_fails_when_destination_exists(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"
            dest.mkdir()

            result = self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                capture_output=True,
                text=True,
                check=False,
            )

            self.assertNotEqual(result.returncode, 0)
            self.assertIn("destination already exists", result.stderr)

    def test_bootstrap_script_dry_run_does_not_create_files(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"

            result = self.run_cli(
                "generate",
                str(dest),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                "--binary-name",
                "demo-cli",
                "--dry-run",
                capture_output=True,
                text=True,
                check=True,
            )

            self.assertFalse(dest.exists())
            self.assertIn("Dry run only. No files were written.", result.stdout)
            self.assertIn("bin path: /ABS/PATH/TO/demo-cli", result.stdout)
            self.assertIn(f"python path: {sys.executable}", result.stdout)
            self.assertIn("read dirs: /ABS/PATH/TO/data,/ABS/PATH/TO/profiles,/ABS/PATH/TO/artifacts", result.stdout)
            self.assertIn("write dirs: /ABS/PATH/TO/artifacts", result.stdout)
            self.assertIn(str(dest / "support" / "Makefile"), result.stdout)
            self.assertIn(str(dest / "support" / "requirements.txt"), result.stdout)
            self.assertIn(str(dest / "support" / "sample-input.txt"), result.stdout)
            self.assertIn(str(dest / "support" / "sample-output" / "README.txt"), result.stdout)
            self.assertIn(str(dest / ".gitignore"), result.stdout)
            self.assertIn(str(dest / "START_HERE.md"), result.stdout)
            self.assertIn(str(dest / "support" / "bin" / "demo-cli"), result.stdout)

    def test_package_docker_creates_deploy_scaffold(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            wrapper_dir = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(wrapper_dir),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                "--binary-name",
                "demo-cli",
                capture_output=True,
                text=True,
                check=True,
            )

            result = self.run_cli(
                "package",
                "docker",
                str(wrapper_dir),
                capture_output=True,
                text=True,
                check=True,
            )

            compose_text = (wrapper_dir / "deploy" / "docker-compose.yml").read_text(encoding="utf-8")
            docker_env_text = (wrapper_dir / "deploy" / "docker.env.example").read_text(encoding="utf-8")
            readme_text = (wrapper_dir / "deploy" / "README.md").read_text(encoding="utf-8")
            runtime_data_text = (wrapper_dir / "deploy" / "runtime" / "data" / "README.txt").read_text(
                encoding="utf-8"
            )

            self.assertIn("Created Docker deployment scaffold", result.stdout)
            self.assertIn("service name: generated-template", result.stdout)
            self.assertIn("image name: generated-template:latest", result.stdout)
            self.assertIn("env prefix: DEMO_SERVER", result.stdout)
            self.assertIn("image: generated-template:latest", compose_text)
            self.assertIn("dockerfile: support/Dockerfile", compose_text)
            self.assertIn("./runtime/data:/workspace/data:ro", compose_text)
            self.assertIn("DEMO_SERVER_BIN=/usr/local/bin/demo-cli", docker_env_text)
            self.assertIn("DEMO_SERVER_ALLOWED_READ_DIRS=/workspace/data,/workspace/profiles,/workspace/artifacts", docker_env_text)
            self.assertIn("docker compose -f docker-compose.yml build generated-template", readme_text)
            self.assertIn("replace the stub binary", readme_text.lower())
            self.assertIn("Read-only input data", runtime_data_text)

    def test_package_docker_prefers_env_binary_name_over_sorted_helper_files(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            wrapper_dir = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(wrapper_dir),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                "--binary-name",
                "demo-cli",
                capture_output=True,
                text=True,
                check=True,
            )

            (wrapper_dir / "support" / "bin" / "aaa-helper").write_text("#!/bin/sh\n", encoding="utf-8")

            self.run_cli(
                "package",
                "docker",
                str(wrapper_dir),
                capture_output=True,
                text=True,
                check=True,
            )

            docker_env_text = (wrapper_dir / "deploy" / "docker.env.example").read_text(encoding="utf-8")
            self.assertIn("DEMO_SERVER_BIN=/usr/local/bin/demo-cli", docker_env_text)

    def test_package_docker_refuses_to_overwrite_without_force(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            wrapper_dir = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(wrapper_dir),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                capture_output=True,
                text=True,
                check=True,
            )

            self.run_cli(
                "package",
                "docker",
                str(wrapper_dir),
                capture_output=True,
                text=True,
                check=True,
            )

            result = self.run_cli(
                "package",
                "docker",
                str(wrapper_dir),
                capture_output=True,
                text=True,
                check=False,
            )

            self.assertNotEqual(result.returncode, 0)
            self.assertIn("deploy directory already exists", result.stderr)

    def test_package_docker_force_allows_overwrite(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            wrapper_dir = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(wrapper_dir),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                capture_output=True,
                text=True,
                check=True,
            )

            self.run_cli(
                "package",
                "docker",
                str(wrapper_dir),
                capture_output=True,
                text=True,
                check=True,
            )

            readme_path = wrapper_dir / "deploy" / "README.md"
            readme_path.write_text("custom\n", encoding="utf-8")

            self.run_cli(
                "package",
                "docker",
                str(wrapper_dir),
                "--force",
                capture_output=True,
                text=True,
                check=True,
            )

            self.assertIn("# Docker Deployment", readme_path.read_text(encoding="utf-8"))

    def test_package_docker_dry_run_does_not_create_files(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            wrapper_dir = Path(tmp_dir) / "generated-template"
            self.run_cli(
                "generate",
                str(wrapper_dir),
                "--server-name",
                "Demo Server",
                "--tool-name",
                "run_profile",
                capture_output=True,
                text=True,
                check=True,
            )

            result = self.run_cli(
                "package",
                "docker",
                str(wrapper_dir),
                "--dry-run",
                capture_output=True,
                text=True,
                check=True,
            )

            self.assertFalse((wrapper_dir / "deploy").exists())
            self.assertIn("Dry run only. No files were written.", result.stdout)
            self.assertIn(str(wrapper_dir / "deploy" / "docker-compose.yml"), result.stdout)
            self.assertIn(str(wrapper_dir / "deploy" / "docker.env.example"), result.stdout)
            self.assertIn(str(wrapper_dir / "deploy" / "runtime" / "artifacts" / "README.txt"), result.stdout)

    def test_legacy_script_shim_still_works(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            dest = Path(tmp_dir) / "generated-template"

            result = subprocess.run(
                [
                    sys.executable,
                    str(SCRIPT_PATH),
                    "generate",
                    str(dest),
                    "--server-name",
                    "Demo Server",
                    "--tool-name",
                    "run_profile",
                ],
                capture_output=True,
                text=True,
                check=True,
            )

            self.assertIn("Created template at", result.stdout)
            self.assertTrue((dest / "mcp_server.py").exists())

    def test_list_templates_reports_python_template(self):
        result = self.run_cli(
            "list-templates",
            capture_output=True,
            text=True,
            check=True,
        )

        self.assertIn("python-cli", result.stdout)
        self.assertIn("Production-style Python MCP wrapper", result.stdout)

    def test_version_flag_reports_package_version(self):
        module = load_module()
        result = self.run_cli(
            "--version",
            capture_output=True,
            text=True,
            check=True,
        )

        self.assertIn(f"mcp-template {module.__version__}", result.stdout)


if __name__ == "__main__":
    unittest.main()
