from __future__ import annotations

from pathlib import Path
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch

from typer.testing import CliRunner

from worai.commands import graph as graph_cmd
from worai.errors import UsageError


def _patch_reset(monkeypatch, side_effect=None):
    mock = AsyncMock(return_value=None, side_effect=side_effect)
    monkeypatch.setattr(graph_cmd, "_reset_me", mock)
    return mock


def _patch_api_key(monkeypatch, api_key="wl_test"):
    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx: (api_key, "default", Path("worai.toml")),
    )


# ---------------------------------------------------------------------------
# SDK unavailable
# ---------------------------------------------------------------------------


def test_graph_reset_raises_usage_error_when_sdk_unavailable(monkeypatch) -> None:
    monkeypatch.setattr(graph_cmd, "_reset_me", None)
    _patch_api_key(monkeypatch)

    result = CliRunner().invoke(graph_cmd.app, ["reset", "--yes"])

    assert result.exit_code == 1
    assert isinstance(result.exception, UsageError)
    assert "wordlift-sdk>=6.12.0" in str(result.exception)


# ---------------------------------------------------------------------------
# API key resolution
# ---------------------------------------------------------------------------


def test_graph_reset_raises_usage_error_when_api_key_missing(monkeypatch) -> None:
    _patch_reset(monkeypatch)
    _patch_api_key(monkeypatch, api_key=None)

    result = CliRunner().invoke(graph_cmd.app, ["reset", "--yes"])

    assert result.exit_code == 1
    assert isinstance(result.exception, UsageError)
    assert "WORDLIFT_API_KEY" in str(result.exception)


def test_graph_reset_raises_usage_error_on_profile_error(monkeypatch) -> None:
    _patch_reset(monkeypatch)
    monkeypatch.setattr(
        graph_cmd,
        "resolve_profile_api_key",
        lambda _ctx: (_ for _ in ()).throw(ValueError("no profile")),
    )

    result = CliRunner().invoke(graph_cmd.app, ["reset", "--yes"])

    assert result.exit_code == 1
    assert isinstance(result.exception, UsageError)
    assert "no profile" in str(result.exception)


# ---------------------------------------------------------------------------
# Confirmation prompt
# ---------------------------------------------------------------------------


def test_graph_reset_prompts_without_yes(monkeypatch) -> None:
    mock = _patch_reset(monkeypatch)
    _patch_api_key(monkeypatch)
    monkeypatch.setattr(graph_cmd.typer, "confirm", lambda *_a, **_k: True)

    result = CliRunner().invoke(graph_cmd.app, ["reset"])

    assert result.exit_code == 0
    mock.assert_awaited_once()


def test_graph_reset_skips_prompt_with_yes(monkeypatch) -> None:
    mock = _patch_reset(monkeypatch)
    _patch_api_key(monkeypatch)

    result = CliRunner().invoke(graph_cmd.app, ["reset", "--yes"])

    assert result.exit_code == 0
    mock.assert_awaited_once()
    assert "Graph reset complete." in result.output


def test_graph_reset_aborts_on_prompt_decline(monkeypatch) -> None:
    mock = _patch_reset(monkeypatch)
    _patch_api_key(monkeypatch)
    monkeypatch.setattr(
        graph_cmd.typer,
        "confirm",
        lambda *_a, **_k: (_ for _ in ()).throw(
            graph_cmd.typer.Abort()
        ),
    )

    result = CliRunner().invoke(graph_cmd.app, ["reset"])

    assert result.exit_code != 0
    mock.assert_not_awaited()


# ---------------------------------------------------------------------------
# Option forwarding
# ---------------------------------------------------------------------------


def test_graph_reset_forwards_keep_flags(monkeypatch) -> None:
    mock = _patch_reset(monkeypatch)
    _patch_api_key(monkeypatch)

    result = CliRunner().invoke(
        graph_cmd.app,
        ["reset", "--yes", "--keep-country", "--keep-language", "--keep-url"],
    )

    assert result.exit_code == 0
    _, kwargs = mock.call_args
    assert kwargs["keep_country"] is True
    assert kwargs["keep_language"] is True
    assert kwargs["keep_url"] is True


def test_graph_reset_default_flags_are_false(monkeypatch) -> None:
    mock = _patch_reset(monkeypatch)
    _patch_api_key(monkeypatch)

    result = CliRunner().invoke(graph_cmd.app, ["reset", "--yes"])

    assert result.exit_code == 0
    _, kwargs = mock.call_args
    assert kwargs["keep_country"] is False
    assert kwargs["keep_language"] is False
    assert kwargs["keep_url"] is False


# ---------------------------------------------------------------------------
# Error wrapping
# ---------------------------------------------------------------------------


def test_graph_reset_wraps_runtime_error(monkeypatch) -> None:
    _patch_reset(monkeypatch, side_effect=RuntimeError("API error"))
    _patch_api_key(monkeypatch)

    result = CliRunner().invoke(graph_cmd.app, ["reset", "--yes"])

    assert result.exit_code == 1
    assert isinstance(result.exception, UsageError)
    assert "Graph reset failed" in str(result.exception)
    assert "API error" in str(result.exception)


# ---------------------------------------------------------------------------
# Warning message
# ---------------------------------------------------------------------------


def test_graph_reset_prints_warning(monkeypatch) -> None:
    _patch_reset(monkeypatch)
    _patch_api_key(monkeypatch)

    result = CliRunner().invoke(graph_cmd.app, ["reset", "--yes"])

    assert "WARNING" in result.output
    assert "destructive" in result.output
