from __future__ import annotations

from argparse import Namespace
from pathlib import Path
from types import SimpleNamespace

import pytest

from krim.app import CliRuntime, KrimCliApp
from krim_sdk.session import SessionSnapshot


class FakeUI:
    def __init__(
        self,
        inputs: list[str | None] | None = None,
        provider: str = "openai",
        model: str = "gpt-5.2",
        secret: str | None = None,
    ):
        self.inputs = list(inputs or [])
        self.provider = provider
        self.model = model
        self.secret = secret
        self.messages: list[str] = []
        self.header_calls: list[dict] = []
        self.runtime_calls: list[dict] = []

    def select_provider(self) -> str:
        return self.provider

    def select_model(self, provider: str, default_model: str) -> str:
        return self.model or default_model

    def prompt_secret(self, label: str) -> str | None:
        self.messages.append(label)
        return self.secret

    def show_header(self, **kwargs) -> None:
        self.header_calls.append(kwargs)

    def show_runtime(self, **kwargs) -> None:
        self.runtime_calls.append(kwargs)

    def write(self, message: str = "") -> None:
        self.messages.append(message)

    def show_bye(self, *, newline: bool = False) -> None:
        prefix = "\n" if newline else ""
        self.messages.append(f"{prefix}[dim]bye[/]")

    def create_session(self):
        return object()

    def prompt_input(self, session=None) -> str | None:
        if not self.inputs:
            return None
        return self.inputs.pop(0)

    def create_event_handler(self, verbose: bool):
        return SimpleNamespace(verbose=verbose)


class FakeAgent:
    def __init__(self):
        self._handler = SimpleNamespace(verbose=True)
        self.messages = []
        self.total_turns = 0
        self.total_tool_calls = 0
        self.run_calls: list[str] = []
        self.compacted = False

    def force_compact(self) -> None:
        self.compacted = True

    def run(self, prompt: str) -> None:
        self.run_calls.append(prompt)


def test_build_runtime_uses_ui_provider_when_missing() -> None:
    ui = FakeUI(provider="openrouter", model="minimax/minimax-m2.5")
    app = KrimCliApp(ui=ui)
    args = Namespace(
        prompt=None,
        loop=False,
        parallel=None,
        provider=None,
        model=None,
        max_turns=None,
        auto_commit=None,
        quiet=False,
    )
    config = SimpleNamespace(max_turns=12, auto_commit=True)

    runtime = app.build_runtime(args, config)

    assert runtime == CliRuntime(
        provider="openrouter",
        model_name="minimax/minimax-m2.5",
        max_turns=12,
        verbose=True,
        do_auto_commit=True,
        is_interactive=True,
    )


@pytest.mark.parametrize(
    ("provider", "env_var", "secret"),
    [
        ("claude", "ANTHROPIC_API_KEY", "anthropic-key"),
        ("openai", "OPENAI_API_KEY", "openai-key"),
        ("openrouter", "OPENROUTER_API_KEY", "openrouter-key"),
    ],
)
def test_maybe_prompt_for_api_key_loads_session_env(monkeypatch, provider: str, env_var: str, secret: str) -> None:
    ui = FakeUI(secret=secret)
    app = KrimCliApp(ui=ui)
    monkeypatch.delenv(env_var, raising=False)
    monkeypatch.setattr("sys.stdin.isatty", lambda: True)

    app._maybe_prompt_for_api_key(provider)

    assert f"[yellow]{provider} requires {env_var}.[/]\n[dim]Enter it for this session only, or leave blank to cancel.[/]" in ui.messages
    assert f"[dim]{env_var} loaded for this process only.[/]" in ui.messages


def test_resolve_prompt_validates_and_prefixes_skill() -> None:
    app = KrimCliApp(ui=FakeUI())

    prompt = app.resolve_prompt("ship it", "deploy", {"deploy": object()})

    assert prompt == "First, invoke the 'deploy' skill to load its instructions. Then: ship it"
    with pytest.raises(ValueError, match="skill 'missing' not found"):
        app.resolve_prompt("ship it", "missing", {})


def test_build_runtime_prefers_resume_session_metadata() -> None:
    app = KrimCliApp(ui=FakeUI(provider="claude"))
    args = Namespace(
        prompt="continue",
        loop=False,
        parallel=None,
        provider=None,
        model=None,
        max_turns=None,
        auto_commit=None,
        quiet=True,
    )
    config = SimpleNamespace(max_turns=8, auto_commit=False)
    resume_session = SessionSnapshot(
        path=Path("/tmp/session"),
        messages=[{"role": "system", "content": "sys"}],
        meta={"provider": "openai", "model": "gpt-5.2"},
        system_prompt="sys",
        provider="openai",
        model="gpt-5.2",
    )

    runtime = app.build_runtime(args, config, resume_session=resume_session)

    assert runtime == CliRuntime(
        provider="openai",
        model_name="gpt-5.2",
        max_turns=8,
        verbose=False,
        do_auto_commit=False,
        is_interactive=False,
    )


def test_run_interactive_session_routes_through_ui_adapter() -> None:
    ui = FakeUI(inputs=["", "/verbose", "fix bug", "quit"])
    app = KrimCliApp(ui=ui)
    agent = FakeAgent()
    config = SimpleNamespace()
    runtime = CliRuntime(
        provider="openai",
        model_name="gpt-5.2",
        max_turns=10,
        verbose=True,
        do_auto_commit=False,
        is_interactive=True,
    )

    app.run_interactive_session(agent, runtime, config)

    assert agent._handler.verbose is False
    assert agent.run_calls == ["fix bug"]
    assert "[dim]verbose: off[/]" in ui.messages
    assert ui.messages[-1] == "[dim]bye[/]"
