from __future__ import annotations
import datetime
import json
from pathlib import Path
from typer.testing import CliRunner
from qrm import cli
from qrm.client import LoginState

runner = CliRunner()


"""
Feature: lr-qrm operator workflows
    Scenario: Check service status
        Given an authenticated engineer
        When they call `qrm status`
        Then they see the service health response.

    Scenario: Show skill
        Given the CLI is installed
        When I run `qrm --show-skill`
        Then SKILL.md content is printed to stdout

    Scenario: Install skill
        Given the CLI is installed
        When I run `qrm --install-skill`
        Then the skill is copied to the target directory

    Scenario: Mutually exclusive skill flags
        Given the CLI is installed
        When I run `qrm --install-skill --show-skill`
        Then exit code 2 is returned
"""

import importlib
import json
from importlib import metadata as importlib_metadata
from importlib.metadata import PackageNotFoundError
from pathlib import Path

from typer.testing import CliRunner

from qrm import __version__
from qrm import cli
from qrm import uut_commands

runner = CliRunner()


def test_status_command_calls_api(monkeypatch, tmp_path: Path) -> None:
    """Status should load stored credentials and print the API response."""

    from qrm.client import LoginState

    state = LoginState(  # type: ignore[call-arg]
        token="TOKEN123",
        username="alice",
        base_url="https://qrm.local/api",
        verify_tls=True,
    )

    config_path = tmp_path / "login.json"
    config_path.write_text(state.model_dump_json())

    captured = {}

    class FakeClient:
        def __init__(self, base_url: str, verify_tls: bool) -> None:  # noqa: D401
            captured["base_url"] = base_url
            captured["verify_tls"] = verify_tls

        def status(self, token: str | None = None):  # noqa: D401
            captured["token"] = token
            return {"healthy": True}

    monkeypatch.setattr(cli, "QrmClient", FakeClient)

    result = runner.invoke(
        cli.app,
        ["status", "--config-path", str(config_path)],
    )

    assert result.exit_code == 0
    assert "healthy" in result.stdout
    assert "True" in result.stdout
    assert captured["token"] == "TOKEN123"
    assert captured["base_url"] == "https://qrm.local/api"
    assert captured["verify_tls"] is False


def test_status_command_supports_json_output(monkeypatch, tmp_path: Path) -> None:
    """Status should emit raw JSON when requested."""

    from qrm.client import LoginState

    state = LoginState(  # type: ignore[call-arg]
        token="TOKEN123",
        username="alice",
        base_url="https://qrm.local/api",
        verify_tls=True,
    )
    config_path = tmp_path / "login.json"
    config_path.write_text(state.model_dump_json())

    class FakeClient:
        def __init__(self, *_: str, **__: str) -> None:  # noqa: D401, ANN001, ANN002, ANN003
            pass

        def status(self, token: str | None = None):  # noqa: D401
            return {"healthy": True, "token": token}

    monkeypatch.setattr(cli, "QrmClient", FakeClient)

    result = runner.invoke(
        cli.app,
        ["status", "--config-path", str(config_path), "--output", "json"],
    )

    assert result.exit_code == 0
    assert json.loads(result.stdout) == {"healthy": True, "token": "TOKEN123"}


def test_status_fails_when_config_missing(tmp_path: Path) -> None:
    """Status should exit with code 1 when config file is missing."""

    missing_path = tmp_path / "nope.json"

    result = runner.invoke(cli.app, ["status", "--config-path", str(missing_path)])

    assert result.exit_code == 1
    assert "No login state found" in result.stdout


def test_status_surfaces_client_error(monkeypatch, tmp_path: Path) -> None:
    """Status should surface QrmClientError nicely."""

    from qrm.client import LoginState

    state = LoginState(  # type: ignore[call-arg]
        token="TOKEN123",
        username="alice",
        base_url="https://qrm.local/api",
        verify_tls=True,
    )
    config_path = tmp_path / "login.json"
    config_path.write_text(state.model_dump_json())

    class ExplodingClient:
        def __init__(self, *_: str, **__: str) -> None:  # noqa: D401
            pass

        def status(self, *_: str, **__: str):
            raise cli.QrmClientError("kaboom")

    monkeypatch.setattr(cli, "QrmClient", ExplodingClient)

    result = runner.invoke(
        cli.app,
        ["status", "--config-path", str(config_path)],
    )

    assert result.exit_code == 1
    assert "Status check failed" in result.stdout


def test_status_renders_list_payload(monkeypatch, tmp_path: Path) -> None:
    """Non-dict payloads should still print via console."""

    from qrm.client import LoginState

    state = LoginState(  # type: ignore[call-arg]
        token="TOKEN123",
        username="alice",
        base_url="https://qrm.local/api",
        verify_tls=True,
    )
    config_path = tmp_path / "login.json"
    config_path.write_text(state.model_dump_json())

    class FakeClient:
        def __init__(self, *_: str, **__: str) -> None:  # noqa: D401
            pass

        def status(self, token: str | None = None):
            return ["ok", token]

    monkeypatch.setattr(cli, "QrmClient", FakeClient)

    result = runner.invoke(
        cli.app,
        ["status", "--config-path", str(config_path)],
    )

    assert result.exit_code == 0
    assert "ok" in result.stdout


def test_cli_displays_help_when_no_command() -> None:
    """Invoking the CLI without arguments returns the Typer help text."""

    result = runner.invoke(cli.app, [])

    assert result.exit_code == 0
    assert "Usage" in result.stdout


def test_version_option_returns_package_version() -> None:
    """`qrm --version` surfaces the packaged version string."""

    result = runner.invoke(cli.app, ["--version"])

    assert result.exit_code == 0
    assert __version__ in result.stdout


def test_version_fallback_reads_pyproject(monkeypatch) -> None:
    """If the package metadata is unavailable, fallback parser should work."""

    def raise_not_found(*_, **__) -> str:  # noqa: ANN002, ANN003
        raise PackageNotFoundError

    monkeypatch.setattr(importlib_metadata, "version", raise_not_found)

    import qrm as qrm_module

    reloaded = importlib.reload(qrm_module)

    assert reloaded._load_version() == __version__


def test_login_command_persists_token(monkeypatch, tmp_path):
    """The login command stores the returned token to disk."""

    captured = {}

    class FakeClient:
        def __init__(self, base_url: str, verify_tls: bool) -> None:  # noqa: D401
            captured["base_url"] = base_url
            captured["verify_tls"] = verify_tls

        def login(self, username: str, password: str) -> str:  # noqa: D401
            captured["username"] = username
            captured["password"] = password
            return "TOKEN123"

    monkeypatch.setattr(cli, "QrmClient", FakeClient)

    config_path = tmp_path / "login.json"
    result = runner.invoke(
        cli.app,
        [
            "login",
            "--username",
            "alice",
            "--password",
            "wonderland",
            "--base-url",
            "https://qrm.local/api",
            "--config-path",
            str(config_path),
        ],
    )

    assert result.exit_code == 0
    data = json.loads(config_path.read_text())
    assert data["token"] == "TOKEN123"
    assert data["username"] == "alice"
    assert data["base_url"] == "https://qrm.local/api"
    assert data["verify_tls"] is True
    assert captured["verify_tls"] is True


def test_login_command_handles_client_errors(monkeypatch, tmp_path):
    """Failures surface as a helpful error with exit code 1."""

    class ExplodingClient:
        def __init__(self, *_, **__):  # noqa: D401, ANN002, ANN003
            pass

        def login(self, *_: str, **__: str) -> str:  # noqa: ANN001, ANN002, ANN003
            raise cli.QrmClientError("boom")

    monkeypatch.setattr(uut_commands, "QrmClient", ExplodingClient)

    config_path = tmp_path / "login.json"
    result = runner.invoke(
        cli.app,
        [
            "login",
            "--username",
            "alice",
            "--password",
            "wonderland",
            "--config-path",
            str(config_path),
        ],
    )

    assert result.exit_code == 1
    assert "Login failed" in result.stdout
    assert not config_path.exists()


def test_show_skill():
    """qrm --show-skill prints SKILL.md content."""
    result = runner.invoke(cli.app, ["--show-skill"])
    assert result.exit_code == 0
    assert "lr-qrm" in result.stdout


def test_install_skill(tmp_path, monkeypatch):
    """qrm --install-skill copies the skill to ~/.claude/skills/."""
    monkeypatch.setattr(Path, "home", lambda: tmp_path)
    monkeypatch.chdir(tmp_path)
    result = runner.invoke(cli.app, ["--install-skill"])
    assert result.exit_code == 0
    assert (tmp_path / ".claude" / "skills" / "lr-qrm").is_dir()


def test_skill_flags_mutually_exclusive():
    """--install-skill and --show-skill together return exit code 2."""
    result = runner.invoke(cli.app, ["--install-skill", "--show-skill"])
    assert result.exit_code == 2
