"""
Module: test_registry
Description: Tests for registry commands

Implements:
    - docs/cli/reference.md#registry-commands
"""

import os
from pathlib import Path
from typing import Any
from unittest.mock import MagicMock, patch

import httpx
import pytest
from typer.testing import CliRunner

from huitzo_cli.auth import TokenStore
from huitzo_cli.commands.registry import registry_app
from huitzo_cli.http_utils import parse_error
from huitzo_cli.config import ConfigManager


def test_publish_requires_manifest(cli_runner: CliRunner, temp_dir: Path) -> None:
    """Test publish requires huitzo.yaml."""
    old_cwd = os.getcwd()
    try:
        os.chdir(temp_dir)
        result = cli_runner.invoke(registry_app, ["publish"])
        assert result.exit_code == 1
        assert "huitzo.yaml" in result.stdout
    finally:
        os.chdir(old_cwd)


def test_publish_requires_auth(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test publish requires authentication after manifest check."""
    # Create a huitzo.yaml
    manifest = temp_dir / "huitzo.yaml"
    manifest.write_text("name: test-pack\nnamespace: test\nversion: 0.0.0\n")

    # Ensure no token exists
    monkeypatch.setattr(TokenStore, "_get_token_path", staticmethod(lambda: temp_dir / "no-token"))
    monkeypatch.setattr(ConfigManager, "_get_config_dir", staticmethod(lambda: temp_dir / "cfg"))

    old_cwd = os.getcwd()
    try:
        os.chdir(temp_dir)
        result = cli_runner.invoke(registry_app, ["publish"])
        assert result.exit_code == 1
        assert "Not authenticated" in result.stdout
    finally:
        os.chdir(old_cwd)


def test_install_from_file(cli_runner: CliRunner, temp_dir: Path) -> None:
    """Test installing from local .whl file."""
    # Create a fake wheel file
    wheel_file = temp_dir / "test-pack-0.0.0-py3-none-any.whl"
    wheel_file.write_text("fake wheel")

    with patch("subprocess.run") as mock_run:
        mock_run.return_value.returncode = 0
        result = cli_runner.invoke(registry_app, ["install", str(wheel_file)])
        assert result.exit_code == 0
        mock_run.assert_called_once()


def test_install_missing_file(cli_runner: CliRunner) -> None:
    """Test installing from missing file."""
    result = cli_runner.invoke(registry_app, ["install", "/nonexistent/pack.whl"])
    assert result.exit_code == 1
    assert "File not found" in result.stdout


def test_install_from_registry(cli_runner: CliRunner) -> None:
    """Test installing from registry shows not available."""
    result = cli_runner.invoke(registry_app, ["install", "my-pack"])
    assert result.exit_code == 1
    assert "not yet available" in result.stdout


def test_list_packs(cli_runner: CliRunner) -> None:
    """Test listing packs."""
    with patch("subprocess.run") as mock_run:
        mock_run.return_value.stdout = "[]"
        mock_run.return_value.returncode = 0

        result = cli_runner.invoke(registry_app, ["list-packs"])
        # Should not error
        assert result.exit_code == 0


def test_run_requires_auth(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test run requires authentication."""
    # Ensure no token exists
    monkeypatch.setattr(TokenStore, "_get_token_path", staticmethod(lambda: temp_dir / "no-token"))
    monkeypatch.setattr(ConfigManager, "_get_config_dir", staticmethod(lambda: temp_dir / "cfg"))

    result = cli_runner.invoke(registry_app, ["run", "my-pack:hello"])
    assert result.exit_code == 1
    assert "Not authenticated" in result.stdout


# --- HTTP flow tests ---


def _mock_response(status_code: int, json_data: dict | None = None, text: str = "") -> MagicMock:
    """Create a mock httpx.Response."""
    resp = MagicMock(spec=httpx.Response)
    resp.status_code = status_code
    resp.text = text
    if json_data is not None:
        resp.json.return_value = json_data
    else:
        resp.json.side_effect = ValueError("No JSON")
    return resp


def test_parse_error_json() -> None:
    """Test parse_error with valid JSON error."""
    resp = _mock_response(400, {"error": {"message": "Bad request"}})
    assert parse_error(resp) == "Bad request"


def test_parse_error_non_json() -> None:
    """Test parse_error with non-JSON response."""
    resp = _mock_response(500, text="Internal Server Error")
    assert parse_error(resp) == "Internal Server Error"


def test_parse_error_string_error() -> None:
    """Test parse_error when error is a plain string."""
    resp = _mock_response(400, {"error": "Something went wrong"})
    assert parse_error(resp) == "Something went wrong"


def test_publish_scope_consistency(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test publish sends scope without @ prefix (backend adds it)."""
    manifest = temp_dir / "huitzo.yaml"
    manifest.write_text("name: test-pack\nnamespace: myns\nversion: 1.0.0\n")

    # Create a fake wheel
    dist_dir = temp_dir / "dist"
    dist_dir.mkdir()
    wheel = dist_dir / "test_pack-1.0.0-py3-none-any.whl"
    wheel.write_text("fake")

    # Setup auth
    token_file = temp_dir / "token"
    token_file.parent.mkdir(parents=True, exist_ok=True)
    token_file.write_text("test-token")
    monkeypatch.setattr(TokenStore, "_get_token_path", staticmethod(lambda: token_file))
    monkeypatch.setattr(ConfigManager, "_get_config_dir", staticmethod(lambda: temp_dir / "cfg"))

    # Mock HTTP calls — 201 returns the pack_id directly
    create_resp = _mock_response(201, {"data": {"id": "pack-1"}})
    upload_resp = _mock_response(201, {"data": {"commands": []}})

    call_count = 0
    post_calls: list[tuple[str, dict[str, Any]]] = []

    def mock_post(url: str, **kwargs: Any) -> MagicMock:
        nonlocal call_count
        call_count += 1
        post_calls.append((url, kwargs))
        if call_count == 1:
            return create_resp
        return upload_resp

    old_cwd = os.getcwd()
    try:
        os.chdir(temp_dir)
        with patch("huitzo_cli.commands.registry.httpx.post", side_effect=mock_post):
            result = cli_runner.invoke(registry_app, ["publish"])
    finally:
        os.chdir(old_cwd)

    assert result.exit_code == 0
    # Verify scope in create request does NOT have @-prefix (backend adds it)
    create_url, create_kwargs = post_calls[0]
    assert create_kwargs["json"]["scope"] == "myns"


def test_publish_network_error(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test publish shows friendly error on network failure."""
    manifest = temp_dir / "huitzo.yaml"
    manifest.write_text("name: test-pack\nnamespace: myns\nversion: 1.0.0\n")

    dist_dir = temp_dir / "dist"
    dist_dir.mkdir()
    (dist_dir / "test-1.0.0-py3-none-any.whl").write_text("fake")

    token_file = temp_dir / "token"
    token_file.parent.mkdir(parents=True, exist_ok=True)
    token_file.write_text("test-token")
    monkeypatch.setattr(TokenStore, "_get_token_path", staticmethod(lambda: token_file))
    monkeypatch.setattr(ConfigManager, "_get_config_dir", staticmethod(lambda: temp_dir / "cfg"))

    old_cwd = os.getcwd()
    try:
        os.chdir(temp_dir)
        with patch(
            "huitzo_cli.commands.registry.httpx.post",
            side_effect=httpx.ConnectError("Connection refused"),
        ):
            result = cli_runner.invoke(registry_app, ["publish"])
    finally:
        os.chdir(old_cwd)

    assert result.exit_code == 1
    assert "Could not connect to backend" in result.stdout


def test_run_network_error(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test run shows friendly error on network failure."""
    token_file = temp_dir / "token"
    token_file.parent.mkdir(parents=True, exist_ok=True)
    token_file.write_text("test-token")
    monkeypatch.setattr(TokenStore, "_get_token_path", staticmethod(lambda: token_file))
    monkeypatch.setattr(ConfigManager, "_get_config_dir", staticmethod(lambda: temp_dir / "cfg"))

    with patch(
        "huitzo_cli.commands.registry.httpx.post",
        side_effect=httpx.ConnectError("Connection refused"),
    ):
        result = cli_runner.invoke(registry_app, ["run", "my-pack:hello"])

    assert result.exit_code == 1
    assert "Could not connect to backend" in result.stdout


def test_run_success(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test successful command execution via run."""
    token_file = temp_dir / "token"
    token_file.parent.mkdir(parents=True, exist_ok=True)
    token_file.write_text("test-token")
    monkeypatch.setattr(TokenStore, "_get_token_path", staticmethod(lambda: token_file))
    monkeypatch.setattr(ConfigManager, "_get_config_dir", staticmethod(lambda: temp_dir / "cfg"))

    mock_resp = _mock_response(200, {"data": {"greeting": "hello world"}})

    with patch("huitzo_cli.commands.registry.httpx.post", return_value=mock_resp):
        result = cli_runner.invoke(registry_app, ["run", "my-pack:hello"])

    assert result.exit_code == 0
    assert "hello world" in result.stdout
