from __future__ import annotations

import importlib
import os
import sys

import pytest

from cli.config import CONFIG_DIR_ENV, CLIConfig, ConfigStore
from shared import BackendRequestError


def _reload_cli_module(monkeypatch: pytest.MonkeyPatch, tmp_path) -> object:
    monkeypatch.setenv(CONFIG_DIR_ENV, str(tmp_path))
    import cli.main as cli_main

    return importlib.reload(cli_main)


def _set_argv(monkeypatch: pytest.MonkeyPatch, args: list[str]) -> None:
    monkeypatch.setattr(sys, "argv", ["ctxme", *args])


def test_cli_error_outputs_clean_message(tmp_path, monkeypatch, capsys) -> None:
    cli_main = _reload_cli_module(monkeypatch, tmp_path)
    monkeypatch.delenv("CTXME_DEBUG", raising=False)
    _set_argv(monkeypatch, ["list"])

    with pytest.raises(SystemExit) as excinfo:
        cli_main.main()

    assert excinfo.value.code == 1
    captured = capsys.readouterr()
    assert "Error:" in captured.err
    assert "Project not specified" in captured.err
    assert "Traceback" not in captured.err
    assert captured.out == ""


def test_debug_env_prints_traceback(tmp_path, monkeypatch, capsys) -> None:
    cli_main = _reload_cli_module(monkeypatch, tmp_path)
    monkeypatch.setenv("CTXME_DEBUG", "true")
    _set_argv(monkeypatch, ["list"])

    with pytest.raises(SystemExit):
        cli_main.main()

    captured = capsys.readouterr()
    assert "Traceback" in captured.err


def test_debug_flag_enables_traceback(tmp_path, monkeypatch, capsys) -> None:
    cli_main = _reload_cli_module(monkeypatch, tmp_path)
    monkeypatch.delenv("CTXME_DEBUG", raising=False)
    _set_argv(monkeypatch, ["--debug", "list"])

    with pytest.raises(SystemExit):
        cli_main.main()

    captured = capsys.readouterr()
    assert os.getenv("CTXME_DEBUG") == "1"
    assert "Traceback" in captured.err


def test_not_found_maps_to_user_message(tmp_path, monkeypatch, capsys) -> None:
    cli_main = _reload_cli_module(monkeypatch, tmp_path)
    monkeypatch.delenv("CTXME_DEBUG", raising=False)

    store = ConfigStore()
    store.save(CLIConfig(default_project="missing", use_mock_client=True))

    _set_argv(monkeypatch, ["list"])
    with pytest.raises(SystemExit):
        cli_main.main()

    captured = capsys.readouterr()
    assert "Item/Project not found:" in captured.err
    assert "missing" in captured.err


def test_debug_shared_error_includes_request_id(tmp_path, monkeypatch, capsys) -> None:
    cli_main = _reload_cli_module(monkeypatch, tmp_path)
    monkeypatch.setenv("CTXME_DEBUG", "1")

    error = BackendRequestError(
        "boom",
        status_code=502,
        request_id="req-123",
        response_body="oops",
        request_url="http://example.test/v1/items",
    )

    def _fail(*_args: object, **_kwargs: object) -> None:
        raise error

    monkeypatch.setattr(cli_main, "app", _fail)

    with pytest.raises(SystemExit):
        cli_main.main()

    captured = capsys.readouterr()
    assert "API error: boom" in captured.err
    assert "Request ID:" in captured.err
    assert "req-123" in captured.err


def test_missing_command_auth_shows_help(tmp_path, monkeypatch, capsys) -> None:
    """Running 'ctxme auth' without subcommand should show error and help."""
    cli_main = _reload_cli_module(monkeypatch, tmp_path)
    _set_argv(monkeypatch, ["auth"])

    with pytest.raises(SystemExit) as excinfo:
        cli_main.main()

    assert excinfo.value.code == 2
    captured = capsys.readouterr()
    # Error message should be present
    assert "Missing command" in captured.err
    # Help text should be appended
    assert "login" in captured.err
    assert "logout" in captured.err
    assert "status" in captured.err
    # Nothing on stdout
    assert captured.out == ""


def test_missing_command_projects_shows_help(tmp_path, monkeypatch, capsys) -> None:
    """Running 'ctxme projects' without subcommand should show error and help."""
    cli_main = _reload_cli_module(monkeypatch, tmp_path)
    _set_argv(monkeypatch, ["projects"])

    with pytest.raises(SystemExit) as excinfo:
        cli_main.main()

    assert excinfo.value.code == 2
    captured = capsys.readouterr()
    # Error message should be present
    assert "Missing command" in captured.err
    # Help text should be appended
    assert "list" in captured.err
    assert "create" in captured.err
    assert "delete" in captured.err
    # Nothing on stdout
    assert captured.out == ""


def test_unknown_command_no_help_appended(tmp_path, monkeypatch, capsys) -> None:
    """Unknown command should show error but NOT append full help."""
    cli_main = _reload_cli_module(monkeypatch, tmp_path)
    _set_argv(monkeypatch, ["auth", "unknown-cmd"])

    with pytest.raises(SystemExit) as excinfo:
        cli_main.main()

    assert excinfo.value.code == 2
    captured = capsys.readouterr()
    # Error should mention unknown command
    assert "No such command" in captured.err
    # But full help listing should NOT be appended (login/logout are subcommands)
    # The error line mentions 'auth --help' hint, but not the full command list
    lines = captured.err.strip().split("\n")
    # Should be short - just usage line, hint, and error
    assert len(lines) < 10
