from __future__ import annotations

import asyncio
from pathlib import Path

import pytest

from aicd.config import AppConfig, PluginsConfig
from aicd.invocation import (
    InvocationContext,
    load_invocation_context,
    should_load_plugins_at_boot,
    subprocess_env_for_child,
)
from aicd.host_tokens import issue_host_nonce, verify_host_nonce


def test_should_load_root_full_default() -> None:
    inv = InvocationContext(
        depth=0,
        spawned_by="user",
        parent_plugin_id=None,
        parent_session=None,
        host_nonce=None,
        plugin_policy="",
    )
    pc = PluginsConfig(boot_plugin_mode="full")
    load, reason = should_load_plugins_at_boot(inv, pc, cli_no_plugins=False)
    assert load is True
    assert "full" in reason or "root" in reason


def test_cli_no_plugins_forces_off() -> None:
    inv = InvocationContext(0, "user", None, None, None, "")
    pc = PluginsConfig(boot_plugin_mode="full")
    load, reason = should_load_plugins_at_boot(inv, pc, cli_no_plugins=True)
    assert load is False
    assert reason == "cli_no_plugins"


def test_nested_default_safe() -> None:
    inv = InvocationContext(1, "user", None, None, None, "")
    pc = PluginsConfig(boot_plugin_mode="full")
    load, reason = should_load_plugins_at_boot(inv, pc, cli_no_plugins=False)
    assert load is False
    assert "nested" in reason


def test_policy_full_overrides_nested() -> None:
    inv = InvocationContext(2, "plugin", None, None, None, "full")
    pc = PluginsConfig(boot_plugin_mode="full")
    load, reason = should_load_plugins_at_boot(inv, pc, cli_no_plugins=False)
    assert load is True


def test_explicit_policy_no_boot() -> None:
    inv = InvocationContext(0, "user", None, None, None, "explicit")
    pc = PluginsConfig(boot_plugin_mode="full")
    load, reason = should_load_plugins_at_boot(inv, pc, cli_no_plugins=False)
    assert load is False


def test_inherit_mode_uses_env_semantics() -> None:
    inv = InvocationContext(0, "user", None, None, None, "")
    pc = PluginsConfig(boot_plugin_mode="inherit")
    load, _ = should_load_plugins_at_boot(inv, pc, cli_no_plugins=False)
    assert load is True
    inv2 = InvocationContext(1, "user", None, None, None, "")
    load2, _ = should_load_plugins_at_boot(inv2, pc, cli_no_plugins=False)
    assert load2 is False


def test_safe_mode_never_loads() -> None:
    inv = InvocationContext(0, "user", None, None, None, "full")
    pc = PluginsConfig(boot_plugin_mode="safe")
    load, _ = should_load_plugins_at_boot(inv, pc, cli_no_plugins=False)
    assert load is False


def test_load_invocation_from_env(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("AICD_INVOCATION_DEPTH", "3")
    monkeypatch.setenv("AICD_SPAWNED_BY", "plugin")
    monkeypatch.setenv("AICD_PARENT_PLUGIN_ID", "demo")
    monkeypatch.setenv("AICD_PLUGIN_POLICY", "off")
    inv = load_invocation_context()
    assert inv.depth == 3
    assert inv.spawned_by == "plugin"
    assert inv.parent_plugin_id == "demo"
    assert inv.plugin_policy == "off"


def test_subprocess_env_for_child(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.delenv("AICD_INVOCATION_DEPTH", raising=False)
    parent = load_invocation_context()
    env = subprocess_env_for_child(
        parent, spawned_by="plugin", parent_plugin_id="p1", extra={"FOO": "bar"}
    )
    assert env["AICD_INVOCATION_DEPTH"] == "1"
    assert env["AICD_SPAWNED_BY"] == "plugin"
    assert env["AICD_PARENT_PLUGIN_ID"] == "p1"
    assert env["FOO"] == "bar"


def test_host_nonce_roundtrip(tmp_path: Path) -> None:
    state = tmp_path / "st"
    tok = issue_host_nonce(state, ttl_sec=120)
    assert ":" in tok
    ok, msg = verify_host_nonce(state, tok)
    assert ok and msg == "ok"
    ok2, _ = verify_host_nonce(state, "bad:bad")
    assert ok2 is False


def test_build_runtime_skips_plugins_when_explicit(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    monkeypatch.setenv("AICD_SKIP_SETUP", "1")
    monkeypatch.setenv("AICD_PLUGIN_POLICY", "explicit")
    from aicd.runtime import build_runtime

    async def _go() -> None:
        ctx = await build_runtime(
            home=tmp_path / "h",
            skip_first_setup=True,
            setup_tty_relaxed=True,
        )
        try:
            summ = ctx.plugin_registry.summarize() if ctx.plugin_registry else []
            assert summ == []
        finally:
            await ctx.shutdown_graceful()

    asyncio.run(_go())
