"""Tests for the InMemorySecretStore."""

from __future__ import annotations

from datetime import datetime, timedelta, timezone

import pytest

from vault.models import AccessPolicy
from vault.store import InMemorySecretStore


class TestSetAndGet:
    async def test_set_and_get_latest(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("db-pass", "hunter2")
        value = await memory_store.get_latest("db-pass")
        assert value == "hunter2"

    async def test_get_nonexistent_returns_none(self, memory_store: InMemorySecretStore):
        assert await memory_store.get_latest("nope") is None

    async def test_set_creates_version_1(self, memory_store: InMemorySecretStore):
        secret = await memory_store.set_secret("key", "value")
        assert secret.version == 1
        assert len(secret.versions) == 1
        assert secret.versions[0].version_number == 1

    async def test_set_with_tags(self, memory_store: InMemorySecretStore):
        secret = await memory_store.set_secret("key", "val", tags={"env": "prod"})
        assert secret.tags == {"env": "prod"}

    async def test_set_with_expires(self, memory_store: InMemorySecretStore):
        exp = datetime.now(timezone.utc) + timedelta(days=30)
        secret = await memory_store.set_secret("key", "val", expires_at=exp)
        assert secret.expires_at == exp

    async def test_set_with_access_policy(self, memory_store: InMemorySecretStore):
        policy = AccessPolicy(allowed_services=["api"])
        secret = await memory_store.set_secret("key", "val", access_policy=policy)
        assert secret.access_policy.allowed_services == ["api"]

    async def test_get_secret_returns_full_object(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "val")
        secret = await memory_store.get_secret("key")
        assert secret is not None
        assert secret.name == "key"

    async def test_decrypt_value_helper(self, memory_store: InMemorySecretStore):
        secret = await memory_store.set_secret("key", "plaintext-value")
        assert memory_store.decrypt_value(secret) == "plaintext-value"


class TestVersioning:
    async def test_rotate_increments_version(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "v1")
        rotated = await memory_store.rotate("key", "v2")
        assert rotated is not None
        assert rotated.version == 2

    async def test_get_specific_version(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "v1")
        await memory_store.rotate("key", "v2")
        v1 = await memory_store.get_secret("key", version=1)
        assert v1 is not None
        assert v1.version == 1
        assert memory_store.decrypt_value(v1) == "v1"

    async def test_get_missing_version_returns_none(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "v1")
        assert await memory_store.get_secret("key", version=99) is None

    async def test_get_versions_list(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "v1")
        await memory_store.rotate("key", "v2")
        await memory_store.rotate("key", "v3")
        versions = await memory_store.get_versions("key")
        assert len(versions) == 3
        assert [v.version_number for v in versions] == [1, 2, 3]

    async def test_rotate_nonexistent_returns_none(self, memory_store: InMemorySecretStore):
        assert await memory_store.rotate("nope", "val") is None


class TestListAndDelete:
    async def test_list_secrets(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("a", "1")
        await memory_store.set_secret("b", "2")
        metas = await memory_store.list_secrets()
        names = {m.name for m in metas}
        assert names == {"a", "b"}

    async def test_list_with_tag_filter(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("prod-db", "x", tags={"env": "prod"})
        await memory_store.set_secret("dev-db", "y", tags={"env": "dev"})
        metas = await memory_store.list_secrets(tag_filter={"env": "prod"})
        assert len(metas) == 1
        assert metas[0].name == "prod-db"

    async def test_delete_secret(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "val")
        assert await memory_store.delete_secret("key") is True
        assert await memory_store.get_latest("key") is None

    async def test_delete_nonexistent_returns_false(self, memory_store: InMemorySecretStore):
        assert await memory_store.delete_secret("nope") is False

    async def test_deleted_secret_not_listed(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "val")
        await memory_store.delete_secret("key")
        metas = await memory_store.list_secrets()
        assert len(metas) == 0

    async def test_deleted_secret_versions_empty(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "val")
        await memory_store.delete_secret("key")
        versions = await memory_store.get_versions("key")
        assert versions == []

    async def test_rotate_deleted_returns_none(self, memory_store: InMemorySecretStore):
        await memory_store.set_secret("key", "val")
        await memory_store.delete_secret("key")
        assert await memory_store.rotate("key", "new") is None
