"""
Module: test_dev
Description: Tests for development session command

Implements:
    - docs/cli/reference.md#huitzo-dev
"""

from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
from fastapi import HTTPException

from huitzo_cli.docs_server.server import DocsServer
from huitzo_cli.sandbox.proxy import LocalSandbox


def test_local_sandbox_init() -> None:
    """Test LocalSandbox initializes with empty state."""
    sandbox = LocalSandbox()
    assert sandbox._commands == {}
    assert sandbox._metadata == {}


def test_local_sandbox_discover_commands() -> None:
    """Test discover_commands scans entry_points."""
    sandbox = LocalSandbox()
    # Won't find any commands in test env, but should not error
    commands = sandbox.discover_commands()
    assert isinstance(commands, dict)


def test_docs_server_init() -> None:
    """Test docs server initialization."""
    server = DocsServer(port=8124)
    assert server.port == 8124
    assert not server.is_running()


def test_docs_server_manifest() -> None:
    """Test getting MCP manifest."""
    server = DocsServer()
    manifest = server.get_mcp_manifest()

    assert "name" in manifest
    assert "version" in manifest
    assert "endpoints" in manifest


def test_docs_server_ensure_docs(temp_dir: Path) -> None:
    """Test docs directory creation."""
    docs_dir = temp_dir / "docs"

    server = DocsServer(docs_dir=docs_dir)

    # Mock git to avoid actual clone
    with patch("subprocess.run") as mock_run:
        mock_run.return_value.returncode = 0

        # Create the directory to avoid clone attempt
        docs_dir.mkdir(parents=True, exist_ok=True)

        result = server.ensure_docs()
        assert result.exists()


def test_docs_server_stop_without_start() -> None:
    """Test stopping server when not running."""
    server = DocsServer()
    server.stop()  # Should not raise error


# --- Sandbox execution tests ---


@pytest.mark.asyncio
async def test_sandbox_execute_success() -> None:
    """Test executing a discovered command."""
    sandbox = LocalSandbox()

    async def fake_cmd(args, ctx):
        return {"result": args.get("name", "world")}

    sandbox._commands["greet"] = fake_cmd
    sandbox._metadata["greet"] = {"name": "greet", "namespace": "test/greet"}

    result = await sandbox.execute("greet", {"name": "Alice"})
    assert result == {"result": "Alice"}


@pytest.mark.asyncio
async def test_sandbox_execute_missing_command() -> None:
    """Test executing a non-existent command raises KeyError."""
    sandbox = LocalSandbox()

    with pytest.raises(KeyError, match="Command not found: nope"):
        await sandbox.execute("nope", {})


@pytest.mark.asyncio
async def test_sandbox_execute_command_error() -> None:
    """Test executing a command that raises propagates the exception."""
    sandbox = LocalSandbox()

    async def failing_cmd(args, ctx):
        raise ValueError("bad input")

    sandbox._commands["fail"] = failing_cmd
    sandbox._metadata["fail"] = {"name": "fail"}

    with pytest.raises(ValueError, match="bad input"):
        await sandbox.execute("fail", {})


def test_sandbox_discovery_failures() -> None:
    """Test that discovery failures are tracked."""
    sandbox = LocalSandbox()
    # After discover, failures should be empty (no broken entry_points in test env)
    sandbox.discover_commands()
    assert isinstance(sandbox.get_discovery_failures(), list)


def test_sandbox_auth_token_stored() -> None:
    """Test that auth token is stored on LocalSandbox."""
    sandbox = LocalSandbox(auth_token="my-token")
    assert sandbox._auth_token == "my-token"


def test_sandbox_stop_without_start() -> None:
    """Test stopping sandbox when not started."""
    sandbox = LocalSandbox()
    sandbox.stop()  # Should not raise


# --- _verify_token security tests ---


def _make_request(auth_header: str | None = None) -> MagicMock:
    """Create a mock Request with optional Authorization header."""
    request = MagicMock()
    headers: dict[str, str] = {}
    if auth_header is not None:
        headers["Authorization"] = auth_header
    request.headers = headers
    return request


def test_verify_token_rejects_missing_header() -> None:
    """Test that missing Authorization header returns 401."""
    sandbox = LocalSandbox(auth_token="secret-token")
    request = _make_request()

    with pytest.raises(HTTPException) as exc_info:
        sandbox._verify_token(request)
    assert exc_info.value.status_code == 401
    assert "Missing" in str(exc_info.value.detail)


def test_verify_token_rejects_invalid_token() -> None:
    """Test that wrong Bearer token returns 401."""
    sandbox = LocalSandbox(auth_token="secret-token")
    request = _make_request("Bearer wrong-token")

    with pytest.raises(HTTPException) as exc_info:
        sandbox._verify_token(request)
    assert exc_info.value.status_code == 401
    assert "Invalid" in str(exc_info.value.detail)


def test_verify_token_accepts_valid_token() -> None:
    """Test that correct Bearer token passes."""
    sandbox = LocalSandbox(auth_token="secret-token")
    request = _make_request("Bearer secret-token")

    # Should not raise
    sandbox._verify_token(request)


def test_verify_token_skips_when_no_auth_configured() -> None:
    """Test that verification is skipped when no auth_token is set."""
    sandbox = LocalSandbox(auth_token=None)
    request = _make_request()  # No header at all

    # Should not raise
    sandbox._verify_token(request)


def test_classify_discovery_failure_module_not_found() -> None:
    """Test enhanced error message for ModuleNotFoundError."""
    sandbox = LocalSandbox()

    error = ModuleNotFoundError("No module named 'storecheck_claims.commands'")
    result = sandbox._classify_discovery_failure("storecheck", error)

    # Should contain error information (at minimum the basic format)
    assert "storecheck" in result
    assert "storecheck_claims.commands" in result
