"""Secret storage backends."""

from __future__ import annotations

import uuid
from abc import ABC, abstractmethod
from datetime import datetime, timezone

from vault.encryption import EncryptionProvider
from vault.models import (
    AccessPolicy,
    Secret,
    SecretMetadata,
    SecretVersion,
)


class SecretStore(ABC):
    """Abstract base class for secret storage backends."""

    @abstractmethod
    async def set_secret(
        self,
        name: str,
        value: str,
        *,
        tags: dict[str, str] | None = None,
        expires_at: datetime | None = None,
        access_policy: AccessPolicy | None = None,
        created_by: str = "",
    ) -> Secret: ...

    @abstractmethod
    async def get_secret(self, name: str, *, version: int | None = None) -> Secret | None: ...

    @abstractmethod
    async def get_latest(self, name: str) -> str | None:
        """Return the decrypted latest value, or None if not found."""
        ...

    @abstractmethod
    async def list_secrets(self, *, tag_filter: dict[str, str] | None = None) -> list[SecretMetadata]: ...

    @abstractmethod
    async def delete_secret(self, name: str) -> bool: ...

    @abstractmethod
    async def get_versions(self, name: str) -> list[SecretVersion]: ...

    @abstractmethod
    async def rotate(self, name: str, new_value: str, *, created_by: str = "") -> Secret | None: ...


class InMemorySecretStore(SecretStore):
    """Dict-backed store with full version history. Good for testing and dev."""

    def __init__(self, encryption: EncryptionProvider) -> None:
        self._encryption = encryption
        self._secrets: dict[str, Secret] = {}

    async def set_secret(
        self,
        name: str,
        value: str,
        *,
        tags: dict[str, str] | None = None,
        expires_at: datetime | None = None,
        access_policy: AccessPolicy | None = None,
        created_by: str = "",
    ) -> Secret:
        now = datetime.now(timezone.utc)
        encrypted = self._encryption.encrypt(value.encode())
        version = SecretVersion(
            version_number=1,
            encrypted_value=encrypted,
            created_at=now,
            created_by=created_by,
        )
        secret = Secret(
            id=uuid.uuid4().hex,
            name=name,
            encrypted_value=encrypted,
            version=1,
            created_at=now,
            updated_at=now,
            expires_at=expires_at,
            tags=tags or {},
            access_policy=access_policy or AccessPolicy(),
            versions=[version],
        )
        self._secrets[name] = secret
        return secret

    async def get_secret(self, name: str, *, version: int | None = None) -> Secret | None:
        secret = self._secrets.get(name)
        if secret is None or secret.deleted:
            return None
        if version is not None:
            matching = [v for v in secret.versions if v.version_number == version]
            if not matching:
                return None
            # Return a copy with that version's encrypted value
            return secret.model_copy(
                update={"encrypted_value": matching[0].encrypted_value, "version": version}
            )
        return secret

    async def get_latest(self, name: str) -> str | None:
        secret = await self.get_secret(name)
        if secret is None:
            return None
        return self._encryption.decrypt(secret.encrypted_value).decode()

    async def list_secrets(self, *, tag_filter: dict[str, str] | None = None) -> list[SecretMetadata]:
        results: list[SecretMetadata] = []
        for secret in self._secrets.values():
            if secret.deleted:
                continue
            if tag_filter:
                if not all(secret.tags.get(k) == v for k, v in tag_filter.items()):
                    continue
            results.append(SecretMetadata.from_secret(secret))
        return results

    async def delete_secret(self, name: str) -> bool:
        secret = self._secrets.get(name)
        if secret is None or secret.deleted:
            return False
        secret.deleted = True
        secret.updated_at = datetime.now(timezone.utc)
        return True

    async def get_versions(self, name: str) -> list[SecretVersion]:
        secret = self._secrets.get(name)
        if secret is None or secret.deleted:
            return []
        return list(secret.versions)

    async def rotate(self, name: str, new_value: str, *, created_by: str = "") -> Secret | None:
        secret = self._secrets.get(name)
        if secret is None or secret.deleted:
            return None
        now = datetime.now(timezone.utc)
        new_version_num = secret.version + 1
        encrypted = self._encryption.encrypt(new_value.encode())
        version = SecretVersion(
            version_number=new_version_num,
            encrypted_value=encrypted,
            created_at=now,
            created_by=created_by,
        )
        secret.versions.append(version)
        secret.encrypted_value = encrypted
        secret.version = new_version_num
        secret.updated_at = now
        return secret

    def decrypt_value(self, secret: Secret) -> str:
        """Convenience: decrypt a secret's current encrypted value."""
        return self._encryption.decrypt(secret.encrypted_value).decode()
