"""Tests for ievo.core.credentials — RSA + AES hybrid encryption."""

from __future__ import annotations

import os
import stat
from pathlib import Path

import pytest

from ievo.core.credentials import (
    decrypt_credentials,
    delete_credential,
    encrypt_credentials,
    ensure_keys,
    get_credential,
    is_sensitive_key,
    list_credentials,
    set_credential,
)


def test_ensure_keys_creates_keypair(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    assert (tmp_path / "ievo_key").exists()
    assert (tmp_path / "ievo_key.pub").exists()


def test_ensure_keys_private_key_permissions(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    mode = os.stat(tmp_path / "ievo_key").st_mode
    assert mode & stat.S_IROTH == 0  # no read for others
    assert mode & stat.S_IWOTH == 0  # no write for others
    assert mode & stat.S_IRGRP == 0  # no read for group


def test_ensure_keys_idempotent(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    content1 = (tmp_path / "ievo_key").read_bytes()
    ensure_keys(tmp_path)  # should not regenerate
    content2 = (tmp_path / "ievo_key").read_bytes()
    assert content1 == content2


def test_encrypt_decrypt_roundtrip(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    data = {"ANTHROPIC_API_KEY": "sk-ant-test123", "GITHUB_TOKEN": "ghp_abc"}
    encrypt_credentials(tmp_path, data)
    result = decrypt_credentials(tmp_path)
    assert result == data


def test_decrypt_missing_file(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    assert decrypt_credentials(tmp_path) == {}


def test_set_get_credential(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    set_credential(tmp_path, "MY_KEY", "my_value")
    assert get_credential(tmp_path, "MY_KEY") == "my_value"


def test_set_overwrites_credential(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    set_credential(tmp_path, "K", "v1")
    set_credential(tmp_path, "K", "v2")
    assert get_credential(tmp_path, "K") == "v2"


def test_delete_credential(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    set_credential(tmp_path, "K", "v")
    assert delete_credential(tmp_path, "K") is True
    assert get_credential(tmp_path, "K") is None


def test_delete_nonexistent_credential(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    assert delete_credential(tmp_path, "NOPE") is False


def test_list_credentials(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    set_credential(tmp_path, "A", "1")
    set_credential(tmp_path, "B", "2")
    keys = list_credentials(tmp_path)
    assert sorted(keys) == ["A", "B"]


def test_list_credentials_empty(tmp_path: Path) -> None:
    ensure_keys(tmp_path)
    assert list_credentials(tmp_path) == []


def test_load_private_key_wrong_type(tmp_path: Path) -> None:
    """Should raise TypeError for non-RSA private key."""
    from unittest.mock import patch

    from ievo.core.credentials import _load_private_key

    ensure_keys(tmp_path)
    with patch("ievo.core.credentials.serialization.load_pem_private_key") as mock_load:
        mock_load.return_value = "not-an-rsa-key"
        with pytest.raises(TypeError, match="Expected RSA private key"):
            _load_private_key(tmp_path)


def test_load_public_key_wrong_type(tmp_path: Path) -> None:
    """Should raise TypeError for non-RSA public key."""
    from unittest.mock import patch

    from ievo.core.credentials import _load_public_key

    ensure_keys(tmp_path)
    with patch("ievo.core.credentials.serialization.load_pem_public_key") as mock_load:
        mock_load.return_value = "not-an-rsa-key"
        with pytest.raises(TypeError, match="Expected RSA public key"):
            _load_public_key(tmp_path)


def test_is_sensitive_key() -> None:
    assert is_sensitive_key("ANTHROPIC_API_KEY") is True
    assert is_sensitive_key("GITHUB_TOKEN") is True
    assert is_sensitive_key("SENTRY_DSN") is True
    assert is_sensitive_key("my_secret") is True
    assert is_sensitive_key("db_password") is True
    assert is_sensitive_key("default_model") is False
    assert is_sensitive_key("marketplace_url") is False
