"""
Character Settings Storage

Stores global secrets in the character's settings.secrets object.
This is the primary storage for agent-level configuration and API keys.

Directly accesses runtime.character.settings rather than using
get_setting()/set_setting() because those methods don't support
object values - they only return primitives.
"""

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

from ..types import (
    SecretConfig,
    SecretContext,
    SecretMetadata,
    SecretLevel,
    StorageBackend,
    StoredSecret,
    EncryptedSecret,
)
from ..crypto import KeyManager, is_encrypted_secret
from .interface import SecretStorageInterface


logger = logging.getLogger(__name__)

SECRETS_KEY = "secrets"
METADATA_KEY = "__secrets_metadata"


class CharacterSettingsStorage(SecretStorageInterface):
    """
    Character settings-based storage for global secrets.

    Secrets are stored in character.settings.secrets with metadata
    tracked separately for configuration management.
    """

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

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

    async def initialize(self) -> None:
        if self._initialized:
            return
        logger.debug("[CharacterSettingsStorage] Initializing")
        self._ensure_settings_structure()
        self._initialized = True
        logger.debug("[CharacterSettingsStorage] Initialized")

    def _ensure_settings_structure(self) -> None:
        """Ensure character.settings.secrets structure exists."""
        character = self._runtime.character
        if not hasattr(character, "settings") or character.settings is None:
            character.settings = {}
        settings = character.settings
        if isinstance(settings, dict) and SECRETS_KEY not in settings:
            settings[SECRETS_KEY] = {}

    def _get_secrets_object(self) -> Dict[str, Any]:
        """Get the secrets dict from character settings."""
        self._ensure_settings_structure()
        settings = self._runtime.character.settings
        if isinstance(settings, dict):
            secrets = settings.get(SECRETS_KEY)
            if isinstance(secrets, dict):
                return secrets
        return {}

    async def exists(self, key: str, context: SecretContext) -> bool:
        self._ensure_settings_structure()
        secrets = self._get_secrets_object()
        return key in secrets

    async def get(self, key: str, context: SecretContext) -> Optional[str]:
        self._ensure_settings_structure()
        secrets = self._get_secrets_object()
        stored = secrets.get(key)

        if stored is None:
            return None

        # Plain string value
        if isinstance(stored, str):
            return stored

        if isinstance(stored, dict):
            # Check if it's a StoredSecret-like dict
            value = stored.get("value")
            config_data = stored.get("config", {})

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

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

            # Plain value in object form
            if isinstance(value, str):
                return value

        return None

    async def set(
        self,
        key: str,
        value: str,
        context: SecretContext,
        config: Optional[Dict] = None,
    ) -> bool:
        self._ensure_settings_structure()
        secrets = self._get_secrets_object()
        existing = secrets.get(key)
        existing_config = None
        if isinstance(existing, dict) and "config" in existing:
            existing_config = existing["config"]

        merged = {}
        if existing_config:
            merged.update(existing_config)
        if config:
            merged.update(config)

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

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

        secrets[key] = {
            "value": stored_value,
            "config": full_config.to_dict(),
        }

        logger.debug(f"[CharacterSettingsStorage] Set secret: {key}")
        return True

    async def delete(self, key: str, context: SecretContext) -> bool:
        self._ensure_settings_structure()
        secrets = self._get_secrets_object()
        if key not in secrets:
            return False
        del secrets[key]
        logger.debug(f"[CharacterSettingsStorage] Deleted secret: {key}")
        return True

    async def list(self, context: SecretContext) -> SecretMetadata:
        secrets = self._get_secrets_object()
        metadata: SecretMetadata = {}
        now = int(time.time() * 1000)

        for key, stored in secrets.items():
            if key == METADATA_KEY:
                continue

            if isinstance(stored, dict) and "config" in stored:
                config_data = stored["config"]
                expires_at = config_data.get("expiresAt")
                if expires_at and expires_at < now:
                    continue
                metadata[key] = SecretConfig.from_dict(config_data)
            elif isinstance(stored, str):
                # Legacy string-only format
                metadata[key] = self._create_default_config(key, SecretContext(
                    level=SecretLevel.GLOBAL,
                    agent_id=self._runtime.agent_id,
                ))

        return metadata

    async def get_config(self, key: str, context: SecretContext) -> Optional[SecretConfig]:
        secrets = self._get_secrets_object()
        stored = secrets.get(key)
        if not stored:
            return None

        if isinstance(stored, dict) and "config" in stored:
            return SecretConfig.from_dict(stored["config"])

        return self._create_default_config(key, context)

    async def update_config(
        self,
        key: str,
        context: SecretContext,
        config: Dict,
    ) -> bool:
        self._ensure_settings_structure()
        secrets = self._get_secrets_object()
        stored = secrets.get(key)
        if not stored:
            return False

        if isinstance(stored, dict) and "config" in stored:
            stored["config"].update(config)
        elif isinstance(stored, str):
            # Upgrade legacy format
            default_config = self._create_default_config(key, SecretContext(
                level=SecretLevel.GLOBAL,
                agent_id=self._runtime.agent_id,
            ))
            merged = default_config.to_dict()
            merged.update(config)
            secrets[key] = {
                "value": stored,
                "config": merged,
            }

        return True

    async def migrate_from_env_vars(self, env_var_prefix: str = "ENV_") -> int:
        """Migrate legacy environment variable format to new format."""
        migrated = 0
        settings = getattr(self._runtime.character, "settings", {}) or {}

        for key, value in settings.items():
            if key.startswith(env_var_prefix) and isinstance(value, str):
                secret_key = key[len(env_var_prefix):]
                ctx = SecretContext(
                    level=SecretLevel.GLOBAL,
                    agent_id=self._runtime.agent_id,
                )
                if not await self.exists(secret_key, ctx):
                    await self.set(secret_key, value, ctx, {
                        "description": f"Migrated from {key}",
                        "encrypted": True,
                    })
                    migrated += 1

        logger.info(f"[CharacterSettingsStorage] Migrated {migrated} env vars to secrets")
        return migrated

    async def get_as_env_var(self, key: str) -> Optional[str]:
        """Get a secret as an environment variable (backward compatibility)."""
        return await self.get(key, SecretContext(
            level=SecretLevel.GLOBAL,
            agent_id=self._runtime.agent_id,
        ))

    async def sync_all_to_env(self) -> int:
        """Sync all secrets to os.environ."""
        import os
        metadata = await self.list(SecretContext(
            level=SecretLevel.GLOBAL,
            agent_id=self._runtime.agent_id,
        ))
        synced = 0
        for key in metadata:
            value = await self.get_as_env_var(key)
            if value is not None:
                os.environ[key] = value
                synced += 1
        return synced
