"""Tests for CLI commands: uninstall, update, version, status."""
from __future__ import annotations

import argparse
import json
from pathlib import Path
from unittest.mock import patch, MagicMock

import pytest

from staso.cli import (
    _remove_staso_hooks,
    _disable_codex_hooks_feature,
    _clean_state_dir,
    cmd_uninstall,
    cmd_status,
    cmd_version,
    cmd_update,
)


# -- uninstall -----------------------------------------------------------------


class TestRemoveStasoHooks:
    def test_removes_staso_hooks_keeps_others(self, tmp_path):
        settings = {
            "hooks": {
                "Stop": [
                    {"hooks": [{"type": "command", "command": "python -m staso.claude_code"}]},
                    {"hooks": [{"type": "command", "command": "my-custom-hook"}]},
                ],
                "SessionStart": [
                    {"hooks": [{"type": "command", "command": "python -m staso.claude_code"}]},
                ],
            },
            "env": {
                "STASO_API_KEY": "ak_test",
                "STASO_ENVIRONMENT": "prod",
                "MY_VAR": "keep-me",
            },
        }
        path = tmp_path / "settings.json"
        path.write_text(json.dumps(settings))

        result = _remove_staso_hooks(path)

        # Custom hook kept
        assert len(result["hooks"]["Stop"]) == 1
        assert "my-custom-hook" in result["hooks"]["Stop"][0]["hooks"][0]["command"]
        # SessionStart removed entirely (was only staso)
        assert "SessionStart" not in result["hooks"]
        # Staso env vars removed, other kept
        assert "STASO_API_KEY" not in result.get("env", {})
        assert result["env"]["MY_VAR"] == "keep-me"

    def test_removes_all_hooks(self, tmp_path):
        settings = {
            "hooks": {
                "Stop": [
                    {"hooks": [{"type": "command", "command": "python -m staso.claude_code"}]},
                ],
            },
            "env": {"STASO_API_KEY": "ak_test"},
        }
        path = tmp_path / "settings.json"
        path.write_text(json.dumps(settings))

        result = _remove_staso_hooks(path)

        assert "hooks" not in result
        assert "env" not in result

    def test_handles_missing_file(self, tmp_path):
        result = _remove_staso_hooks(tmp_path / "nonexistent.json")
        assert result == {}


class TestDisableCodexHooksFeature:
    def test_removes_feature_flag(self, tmp_path, monkeypatch):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)
        codex_dir = tmp_path / ".codex"
        codex_dir.mkdir(parents=True)
        (codex_dir / "config.toml").write_text(
            '[features]\ncodex_hooks = true\nother_flag = true\n'
        )

        _disable_codex_hooks_feature("global")

        content = (codex_dir / "config.toml").read_text()
        assert "codex_hooks" not in content
        assert "other_flag" in content

    def test_removes_empty_features_section(self, tmp_path, monkeypatch):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)
        codex_dir = tmp_path / ".codex"
        codex_dir.mkdir(parents=True)
        (codex_dir / "config.toml").write_text('[features]\ncodex_hooks = true\n')

        _disable_codex_hooks_feature("global")

        content = (codex_dir / "config.toml").read_text()
        assert "[features]" not in content

    def test_noop_when_not_present(self, tmp_path, monkeypatch):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)
        codex_dir = tmp_path / ".codex"
        codex_dir.mkdir(parents=True)
        original = 'model = "gpt-5.4"\n'
        (codex_dir / "config.toml").write_text(original)

        _disable_codex_hooks_feature("global")

        assert (codex_dir / "config.toml").read_text() == original

    def test_noop_when_file_missing(self, tmp_path, monkeypatch):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)
        _disable_codex_hooks_feature("global")  # must not raise


class TestCleanStateDir:
    def test_removes_state_files(self, tmp_path, monkeypatch):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)
        state_dir = tmp_path / ".staso" / "codex"
        state_dir.mkdir(parents=True)
        (state_dir / "session-1.json").write_text("{}")
        (state_dir / "session-2.json").write_text("{}")

        count = _clean_state_dir("codex")
        assert count == 2
        assert not state_dir.exists()

    def test_returns_zero_for_missing_dir(self, tmp_path, monkeypatch):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)
        assert _clean_state_dir("codex") == 0


class TestCmdUninstall:
    def test_uninstalls_codex(self, tmp_path, monkeypatch):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)

        # Setup codex hooks
        codex_dir = tmp_path / ".codex"
        codex_dir.mkdir(parents=True)
        hooks = {
            "hooks": {
                "Stop": [{"hooks": [{"type": "command", "command": "python -m staso.codex"}]}],
            },
        }
        (codex_dir / "hooks.json").write_text(json.dumps(hooks))
        (codex_dir / "config.toml").write_text("[features]\ncodex_hooks = true\n")

        args = argparse.Namespace(target="codex")
        result = cmd_uninstall(args)
        assert result == 0

        # Hooks removed
        remaining = json.loads((codex_dir / "hooks.json").read_text())
        assert not remaining.get("hooks")

        # Feature flag removed
        config = (codex_dir / "config.toml").read_text()
        assert "codex_hooks" not in config

    def test_reports_when_nothing_found(self, tmp_path, monkeypatch, capsys):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)
        args = argparse.Namespace(target=None)
        result = cmd_uninstall(args)
        assert result == 0
        assert "Nothing to uninstall" in capsys.readouterr().out


# -- status --------------------------------------------------------------------


class TestCmdStatus:
    def test_shows_configured_agent(self, tmp_path, monkeypatch, capsys):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)

        claude_dir = tmp_path / ".claude"
        claude_dir.mkdir(parents=True)
        settings = {
            "hooks": {
                "Stop": [{"hooks": [{"type": "command", "command": "python -m staso.claude_code"}]}],
            },
            "env": {"STASO_API_KEY": "ak_test", "STASO_ENVIRONMENT": "prod"},
        }
        (claude_dir / "settings.json").write_text(json.dumps(settings))

        args = argparse.Namespace(target="claude-code")
        result = cmd_status(args)
        assert result == 0
        out = capsys.readouterr().out
        assert "Claude Code" in out
        assert "1 hook events" in out

    def test_shows_not_configured(self, tmp_path, monkeypatch, capsys):
        monkeypatch.setattr("staso.cli.Path.home", lambda: tmp_path)
        args = argparse.Namespace(target="codex")
        result = cmd_status(args)
        assert result == 0
        assert "not configured" in capsys.readouterr().out


# -- version -------------------------------------------------------------------


class TestCmdVersion:
    def test_prints_version(self, capsys):
        args = argparse.Namespace()
        result = cmd_version(args)
        assert result == 0
        out = capsys.readouterr().out
        assert "staso" in out


# -- update --------------------------------------------------------------------


class TestCmdUpdate:
    def test_runs_pip_and_sync(self, capsys):
        mock_run = MagicMock()
        mock_run.return_value = MagicMock(returncode=0, stderr="", stdout="")
        with patch("subprocess.run", mock_run), \
             patch.object(__import__("staso.cli", fromlist=["cmd_sync"]), "cmd_sync", return_value=0):
            args = argparse.Namespace()
            result = cmd_update(args)
        assert result == 0
        mock_run.assert_called_once()
        assert "pip" in mock_run.call_args[0][0][2]

    def test_fails_on_pip_error(self, capsys):
        mock_run = MagicMock()
        mock_run.return_value = MagicMock(returncode=1, stderr="error", stdout="")
        with patch("subprocess.run", mock_run):
            args = argparse.Namespace()
            result = cmd_update(args)
        assert result == 1
