"""
Module: test_secrets
Description: Tests for secrets management

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

from pathlib import Path
from unittest.mock import patch

import pytest
from typer.testing import CliRunner

from huitzo_cli.commands.secrets import SecretsStore, secrets_app


def test_secrets_store_set_file_fallback(temp_dir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Test setting a secret via file fallback when keyring unavailable."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        SecretsStore.set("test_key", "test_value")

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        value = SecretsStore.get("test_key")
    assert value == "test_value"


def test_secrets_store_get_file_fallback(temp_dir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Test getting a secret from file fallback."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        SecretsStore.set("test_key", "test_value")
        value = SecretsStore.get("test_key")

    assert value == "test_value"


def test_secrets_store_remove(temp_dir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Test removing a secret returns True."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        SecretsStore.set("test_key", "test_value")
        removed = SecretsStore.remove("test_key")

    assert removed is True
    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        assert SecretsStore.get("test_key") is None


def test_secrets_store_remove_missing(temp_dir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Test removing a nonexistent secret returns False."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        removed = SecretsStore.remove("nonexistent")
    assert removed is False


def test_secrets_store_file_permissions(temp_dir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Test file permissions are 0o600 in fallback mode."""
    import os
    import stat

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        SecretsStore.set("perm_test", "value")

    secrets_file = temp_dir / "secrets" / "default.json"
    if os.name != "nt":
        mode = stat.S_IMODE(secrets_file.stat().st_mode)
        assert mode == 0o600


def test_secrets_command_set_interactive(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test set command always prompts interactively (W1: no positional value)."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        # Simulate interactive input (value prompted)
        result = cli_runner.invoke(secrets_app, ["set", "test_key"], input="my-secret\n")

    assert result.exit_code == 0
    assert "Secret 'test_key' set" in result.stdout

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        assert SecretsStore.get("test_key") == "my-secret"


def test_secrets_command_set_no_positional_value(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test 'secrets set KEY VALUE' as positional arg is rejected (W1)."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        # Passing extra positional arg should fail
        result = cli_runner.invoke(secrets_app, ["set", "test_key", "test_value"])

    # Typer should reject the unexpected positional argument
    assert result.exit_code != 0


def test_secrets_command_list(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test listing secrets via command."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        SecretsStore.set("test_key", "test_value")

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        result = cli_runner.invoke(secrets_app, ["list"])

    assert result.exit_code == 0
    assert "test_key" in result.stdout


def test_secrets_command_remove(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test removing secret via command."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        SecretsStore.set("test_key", "test_value")

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        result = cli_runner.invoke(secrets_app, ["remove", "test_key"])

    assert result.exit_code == 0
    assert "removed" in result.stdout


def test_secrets_command_remove_missing(
    cli_runner: CliRunner, temp_dir: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Test removing nonexistent secret prints not-found message."""

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=False):
        result = cli_runner.invoke(secrets_app, ["remove", "nonexistent"])

    assert result.exit_code == 1
    assert "not found" in result.stdout


def test_secrets_store_keyring_set_get(temp_dir: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Test secret set/get round-trip with mocked keyring."""
    import sys
    from unittest.mock import MagicMock

    def mock_get_secrets_dir() -> Path:
        return temp_dir / "secrets"

    monkeypatch.setattr(SecretsStore, "_get_secrets_dir", mock_get_secrets_dir)

    keyring_store: dict[str, str] = {}

    # Create a mock keyring module in sys.modules
    mock_keyring = MagicMock()
    mock_keyring.set_password = lambda service, account, password: keyring_store.__setitem__(
        f"{service}:{account}", password
    )
    mock_keyring.get_password = lambda service, account: keyring_store.get(f"{service}:{account}")
    monkeypatch.setitem(sys.modules, "keyring", mock_keyring)

    with patch("huitzo_cli.commands.secrets._keyring_available", return_value=True):
        SecretsStore.set("api_key", "super-secret")
        value = SecretsStore.get("api_key")

    assert value == "super-secret"
