#!/usr/bin/env python3
# Timestamp: 2026-03-21
# File: tests/test__cli.py

"""Tests for scitex_app/_cli/_main.py — CLI commands via Click's CliRunner."""

from __future__ import annotations

import json

import pytest

click = pytest.importorskip("click")
from click.testing import CliRunner

from scitex_app._cli._main import main


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def runner():
    return CliRunner()


@pytest.fixture
def temp_dir(tmp_path):
    """A temporary directory usable as a file backend root."""
    return tmp_path


# ---------------------------------------------------------------------------
# Tests: top-level main group
# ---------------------------------------------------------------------------


class TestMainGroup:
    def test_help_flag(self, runner):
        result = runner.invoke(main, ["--help"])
        assert result.exit_code == 0
        assert "SciTeX App SDK" in result.output

    def test_h_flag_alias(self, runner):
        result = runner.invoke(main, ["-h"])
        assert result.exit_code == 0
        assert "SciTeX App SDK" in result.output

    def test_version_flag(self, runner):
        result = runner.invoke(main, ["--version"])
        assert result.exit_code == 0
        assert "scitex-app" in result.output.lower()

    def test_no_args_shows_help(self, runner):
        result = runner.invoke(main, [])
        assert result.exit_code == 0
        assert "Usage" in result.output or "SciTeX" in result.output

    def test_help_recursive_flag(self, runner):
        result = runner.invoke(main, ["--help-recursive"])
        assert result.exit_code == 0
        # Should show help content for subcommands
        assert "Command:" in result.output or "SciTeX" in result.output


# ---------------------------------------------------------------------------
# Tests: list command
# ---------------------------------------------------------------------------


class TestListCommand:
    def test_list_empty_directory(self, runner, temp_dir):
        result = runner.invoke(main, ["list", "--root", str(temp_dir)])
        assert result.exit_code == 0
        assert result.output.strip() == ""

    def test_list_shows_files(self, runner, temp_dir):
        (temp_dir / "hello.txt").write_text("world", encoding="utf-8")
        (temp_dir / "config.yaml").write_text("key: value", encoding="utf-8")
        result = runner.invoke(main, ["list", "--root", str(temp_dir)])
        assert result.exit_code == 0
        assert "hello.txt" in result.output
        assert "config.yaml" in result.output

    def test_list_with_extension_filter(self, runner, temp_dir):
        (temp_dir / "a.txt").write_text("a")
        (temp_dir / "b.yaml").write_text("b: 1")
        result = runner.invoke(
            main, ["list", "--root", str(temp_dir), "--ext", ".yaml"]
        )
        assert result.exit_code == 0
        assert "b.yaml" in result.output
        assert "a.txt" not in result.output

    def test_list_subdirectory(self, runner, temp_dir):
        subdir = temp_dir / "sub"
        subdir.mkdir()
        (subdir / "note.md").write_text("# Notes")
        result = runner.invoke(main, ["list", "sub", "--root", str(temp_dir)])
        assert result.exit_code == 0
        assert "note.md" in result.output

    def test_list_help(self, runner):
        result = runner.invoke(main, ["list", "--help"])
        assert result.exit_code == 0
        assert "directory" in result.output.lower() or "List" in result.output


# ---------------------------------------------------------------------------
# Tests: exists command
# ---------------------------------------------------------------------------


class TestExistsCommand:
    def test_exists_true(self, runner, temp_dir):
        (temp_dir / "present.txt").write_text("here")
        result = runner.invoke(main, ["exists", "present.txt", "--root", str(temp_dir)])
        assert "true" in result.output
        assert result.exit_code == 0

    def test_exists_false(self, runner, temp_dir):
        result = runner.invoke(main, ["exists", "absent.txt", "--root", str(temp_dir)])
        assert "false" in result.output
        assert result.exit_code == 1

    def test_exists_help(self, runner):
        result = runner.invoke(main, ["exists", "--help"])
        assert result.exit_code == 0


# ---------------------------------------------------------------------------
# Tests: read command
# ---------------------------------------------------------------------------


class TestReadCommand:
    def test_read_text_file(self, runner, temp_dir):
        (temp_dir / "data.txt").write_text("hello world", encoding="utf-8")
        result = runner.invoke(main, ["read", "data.txt", "--root", str(temp_dir)])
        assert result.exit_code == 0
        assert "hello world" in result.output

    def test_read_missing_file_fails(self, runner, temp_dir):
        result = runner.invoke(main, ["read", "missing.txt", "--root", str(temp_dir)])
        assert result.exit_code != 0

    def test_read_help(self, runner):
        result = runner.invoke(main, ["read", "--help"])
        assert result.exit_code == 0
        assert "path" in result.output.lower() or "Read" in result.output


# ---------------------------------------------------------------------------
# Tests: app subgroup
# ---------------------------------------------------------------------------


class TestAppSubgroup:
    def test_app_help(self, runner):
        result = runner.invoke(main, ["app", "--help"])
        assert result.exit_code == 0
        assert "app" in result.output.lower()

    def test_app_validate_help(self, runner):
        result = runner.invoke(main, ["app", "validate", "--help"])
        assert result.exit_code == 0

    def test_app_init_help(self, runner):
        result = runner.invoke(main, ["app", "init", "--help"])
        assert result.exit_code == 0

    def test_app_validate_passes_for_minimal_valid_app(self, runner, temp_dir):
        """App validate should pass for a directory that meets minimum requirements."""
        import json

        # Create minimal app that validate() would accept (embedded, so fewer checks)
        manifest = {
            "name": "test_app",
            "slug": "test-app",
            "label": "Test",
            "version": "1.0.0",
            "icon": "fas fa-flask",
            "license": "MIT",
            "embedded_package": True,
            "dependencies": {"python": []},
        }
        (temp_dir / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")
        (temp_dir / "views.py").touch()
        (temp_dir / "urls.py").touch()
        result = runner.invoke(main, ["app", "validate", str(temp_dir)])
        # Should succeed (exit_code 0) or at minimum not crash
        # The exact passing depends on validate() checks
        assert result.exit_code in (0, 1)

    def test_app_validate_fails_for_missing_manifest(self, runner, temp_dir):
        result = runner.invoke(main, ["app", "validate", str(temp_dir)])
        # Missing manifest.json should cause a non-zero exit
        assert result.exit_code != 0

    def test_app_init_creates_files(self, runner, temp_dir):
        target = temp_dir / "my_app"
        target.mkdir()
        result = runner.invoke(
            main,
            ["app", "init", str(target), "--name", "my_app"],
        )
        # Should succeed and create files
        assert result.exit_code == 0


# ---------------------------------------------------------------------------
# Tests: mcp subgroup
# ---------------------------------------------------------------------------


class TestMcpSubgroup:
    def test_mcp_help(self, runner):
        result = runner.invoke(main, ["mcp", "--help"])
        assert result.exit_code == 0

    def test_mcp_installation(self, runner):
        result = runner.invoke(main, ["mcp", "installation"])
        assert result.exit_code == 0
        assert "pip install" in result.output

    def test_mcp_doctor(self, runner):
        result = runner.invoke(main, ["mcp", "doctor"])
        assert result.exit_code == 0
        # Output should mention dependencies
        assert "fastmcp" in result.output.lower() or "MCP" in result.output

    def test_mcp_list_tools_help(self, runner):
        result = runner.invoke(main, ["mcp", "list-tools", "--help"])
        assert result.exit_code == 0


# ---------------------------------------------------------------------------
# Tests: list-python-apis command
# ---------------------------------------------------------------------------


class TestListPythonApis:
    def test_list_python_apis_runs(self, runner):
        result = runner.invoke(main, ["list-python-apis"])
        assert result.exit_code == 0
        assert "scitex" in result.output.lower()

    def test_list_python_apis_json_output(self, runner):
        result = runner.invoke(main, ["list-python-apis", "--json"])
        assert result.exit_code == 0
        data = json.loads(result.output)
        assert isinstance(data, list)
        assert len(data) > 0

    def test_list_python_apis_root_only(self, runner):
        result = runner.invoke(main, ["list-python-apis", "--root-only"])
        assert result.exit_code == 0

    def test_list_python_apis_help(self, runner):
        result = runner.invoke(main, ["list-python-apis", "--help"])
        assert result.exit_code == 0


# EOF
