"""
Component Secret Storage

Stores user-level secrets as Components in the ElizaOS database.
Each user's secrets are isolated via the component's entity_id.
"""

import logging
import time
from typing import Optional, Dict, Any, List

from ..types import (
    SecretConfig,
    SecretContext,
    SecretMetadata,
    StorageBackend,
    PermissionDeniedError,
    StorageError,
)
from ..crypto import KeyManager, is_encrypted_secret
from .interface import SecretStorageInterface


logger = logging.getLogger(__name__)

COMPONENT_TYPE = "secret"


class ComponentSecretStorage(SecretStorageInterface):
    """
    Component-based storage for user-level secrets.

    Each secret is stored as a Component with type='secret' and entity_id
    set to the user's ID, providing natural isolation per user.
    """

    def __init__(self, runtime: Any, key_manager: KeyManager):
        self._runtime = runtime
        self._key_manager = key_manager

    @property
    def storage_type(self) -> StorageBackend:
        return StorageBackend.COMPONENT

    async def initialize(self) -> None:
        logger.debug("[ComponentSecretStorage] Initialized")

    async def _find_secret_component(self, user_id: str, key: str) -> Optional[Any]:
        """Find a secret component for a user by key."""
        components = await self._runtime.get_components(user_id)
        for component in components:
            if getattr(component, "type", None) != COMPONENT_TYPE:
                continue
            data = getattr(component, "data", None)
            if isinstance(data, dict) and data.get("key") == key:
                return component
        return None

    def _check_permission(self, context: SecretContext, operation: str) -> None:
        """Check that the requester has permission (user can only access own secrets)."""
        if context.requester_id and context.requester_id != context.user_id:
            from ..types import SecretPermissionType
            perm = SecretPermissionType(operation)
            raise PermissionDeniedError(f"user:{context.user_id}", perm, context)

    async def exists(self, key: str, context: SecretContext) -> bool:
        if not context.user_id:
            return False
        component = await self._find_secret_component(context.user_id, key)
        return component is not None

    async def get(self, key: str, context: SecretContext) -> Optional[str]:
        if not context.user_id:
            logger.warn("[ComponentSecretStorage] Cannot get secret without userId")
            return None

        self._check_permission(context, "read")

        component = await self._find_secret_component(context.user_id, key)
        if not component:
            return None

        data = getattr(component, "data", None)
        if not isinstance(data, dict):
            return None

        # Check expiration
        config_data = data.get("config", {})
        expires_at = config_data.get("expiresAt")
        if expires_at and expires_at < int(time.time() * 1000):
            await self.delete(key, context)
            return None

        value = data.get("value")

        # Handle encrypted value
        if is_encrypted_secret(value):
            return self._key_manager.decrypt(value)

        if isinstance(value, str):
            return value

        return None

    async def set(
        self,
        key: str,
        value: str,
        context: SecretContext,
        config: Optional[Dict] = None,
    ) -> bool:
        if not context.user_id:
            raise StorageError("Cannot set user secret without userId")

        self._check_permission(context, "write")

        existing_component = await self._find_secret_component(context.user_id, key)
        existing_config = None
        if existing_component:
            data = getattr(existing_component, "data", None)
            if isinstance(data, dict) and "config" in data:
                existing_config = data["config"]

        merged = {}
        if existing_config:
            merged.update(existing_config)
        if config:
            merged.update(config)
        merged["ownerId"] = context.user_id

        full_config = self._create_default_config(key, context, merged)

        should_encrypt = full_config.encrypted is not False
        if should_encrypt:
            stored_value = self._key_manager.encrypt(value).to_dict()
        else:
            stored_value = value

        component_data = {
            "key": key,
            "value": stored_value,
            "config": full_config.to_dict(),
            "updatedAt": int(time.time() * 1000),
        }

        if existing_component:
            existing_component.data = component_data
            await self._runtime.update_component(existing_component)
            logger.debug(
                f"[ComponentSecretStorage] Updated secret: {key} for user: {context.user_id}"
            )
        else:
            # Create new component
            import hashlib
            component_id = hashlib.sha256(
                f"{self._runtime.agent_id}-{context.user_id}-secret-{key}".encode()
            ).hexdigest()

            new_component = type("Component", (), {
                "id": component_id,
                "created_at": int(time.time() * 1000),
                "entity_id": context.user_id,
                "agent_id": self._runtime.agent_id,
                "room_id": self._runtime.agent_id,
                "world_id": self._runtime.agent_id,
                "source_entity_id": context.user_id,
                "type": COMPONENT_TYPE,
                "data": component_data,
            })()

            await self._runtime.create_component(new_component)
            logger.debug(
                f"[ComponentSecretStorage] Created secret: {key} for user: {context.user_id}"
            )

        return True

    async def delete(self, key: str, context: SecretContext) -> bool:
        if not context.user_id:
            return False

        self._check_permission(context, "delete")

        component = await self._find_secret_component(context.user_id, key)
        if not component:
            return False

        await self._runtime.delete_component(component.id)
        logger.debug(
            f"[ComponentSecretStorage] Deleted secret: {key} for user: {context.user_id}"
        )
        return True

    async def list(self, context: SecretContext) -> SecretMetadata:
        if not context.user_id:
            return {}

        self._check_permission(context, "read")

        components = await self._runtime.get_components(context.user_id)
        metadata: SecretMetadata = {}
        now = int(time.time() * 1000)

        for component in components:
            if getattr(component, "type", None) != COMPONENT_TYPE:
                continue
            data = getattr(component, "data", None)
            if not isinstance(data, dict) or "key" not in data or "config" not in data:
                continue

            config_data = data["config"]
            expires_at = config_data.get("expiresAt")
            if expires_at and expires_at < now:
                continue

            metadata[data["key"]] = SecretConfig.from_dict(config_data)

        return metadata

    async def get_config(self, key: str, context: SecretContext) -> Optional[SecretConfig]:
        if not context.user_id:
            return None

        component = await self._find_secret_component(context.user_id, key)
        if not component:
            return None

        data = getattr(component, "data", None)
        if isinstance(data, dict) and "config" in data:
            return SecretConfig.from_dict(data["config"])

        return None

    async def update_config(
        self,
        key: str,
        context: SecretContext,
        config: Dict,
    ) -> bool:
        if not context.user_id:
            return False

        self._check_permission(context, "write")

        component = await self._find_secret_component(context.user_id, key)
        if not component:
            return False

        data = getattr(component, "data", None)
        if not isinstance(data, dict):
            return False

        if "config" in data:
            data["config"].update(config)
        data["updatedAt"] = int(time.time() * 1000)

        component.data = data
        await self._runtime.update_component(component)
        return True

    async def list_keys(self, user_id: str) -> List[str]:
        """Get all secret keys for a user."""
        components = await self._runtime.get_components(user_id)
        keys = []
        for component in components:
            if getattr(component, "type", None) != COMPONENT_TYPE:
                continue
            data = getattr(component, "data", None)
            if isinstance(data, dict) and "key" in data:
                keys.append(data["key"])
        return keys

    async def delete_all_for_user(self, user_id: str) -> int:
        """Delete all secrets for a user."""
        components = await self._runtime.get_components(user_id)
        deleted = 0
        for component in components:
            if getattr(component, "type", None) != COMPONENT_TYPE:
                continue
            await self._runtime.delete_component(component.id)
            deleted += 1
        logger.info(f"[ComponentSecretStorage] Deleted {deleted} secrets for user: {user_id}")
        return deleted

    async def count_for_user(self, user_id: str) -> int:
        """Count secrets for a user."""
        components = await self._runtime.get_components(user_id)
        return sum(1 for c in components if getattr(c, "type", None) == COMPONENT_TYPE)
