"""Local JSON storage for CLI persistent data.

Provides a scalable storage system for various CLI features like command history,
preferences, cached data, etc. Data is stored in ~/.alloy-runtime/storage.json.
"""

import json
import os
from pathlib import Path
from typing import Any, cast


CONFIG_DIR = Path.home() / ".alloy-runtime"
STORAGE_FILE = CONFIG_DIR / "storage.json"

# Default limits for various storage features
MAX_COMMAND_HISTORY = 15


class LocalStorage:
    """JSON-based local storage for CLI persistent data.

    Provides getter/setter methods for various storage features.
    Data is automatically persisted to ~/.alloy-runtime/storage.json.

    Example:
        storage = LocalStorage()

        # Command history
        storage.add_command("Hello, how are you?")
        history = storage.get_command_history()

        # Future features can add their own sections
        storage.set("preferences", "theme", "dark")
        theme = storage.get("preferences", "theme", default="light")
    """

    def __init__(self) -> None:
        self._data: dict[str, Any] = self._load()

    def _load(self) -> dict[str, Any]:
        """Load storage data from disk."""
        if not STORAGE_FILE.exists():
            return self._default_data()

        try:
            with open(STORAGE_FILE, encoding="utf-8") as f:
                data = json.load(f)
                # Ensure all expected keys exist
                return self._merge_with_defaults(data)
        except (json.JSONDecodeError, OSError):
            # If file is corrupted, start fresh
            return self._default_data()

    def _default_data(self) -> dict[str, Any]:
        """Return default storage structure."""
        return {
            "command_history": [],
            "preferences": {},
            "cache": {},
        }

    def _merge_with_defaults(self, data: dict[str, Any]) -> dict[str, Any]:
        """Merge loaded data with defaults to ensure all keys exist."""
        defaults = self._default_data()
        for key, value in defaults.items():
            if key not in data:
                data[key] = value
        return data

    def _save(self) -> None:
        """Save storage data to disk."""
        CONFIG_DIR.mkdir(exist_ok=True, mode=0o700)

        with open(STORAGE_FILE, "w", encoding="utf-8") as f:
            json.dump(self._data, f, indent=2)

        # Set secure file permissions
        os.chmod(STORAGE_FILE, 0o600)

    # -------------------------------------------------------------------------
    # Command History Methods
    # -------------------------------------------------------------------------

    def get_command_history(self) -> list[str]:
        """Get the command history list.

        Returns:
            List of previously sent messages, most recent last.
        """
        history = self._data.get("command_history", [])
        if isinstance(history, list):
            return [str(item) for item in cast(list[Any], history)]
        return []

    def add_command(self, command: str) -> None:
        """Add a command to history.

        Maintains a maximum of MAX_COMMAND_HISTORY entries.
        Duplicates are allowed (same message can appear multiple times).

        Args:
            command: The user message to add to history.
        """
        command = command.strip()
        if not command:
            return

        history_data = self._data.get("command_history", [])
        history: list[str]
        if isinstance(history_data, list):
            history = [str(item) for item in cast(list[Any], history_data)]
        else:
            history = []
        history.append(command)

        # Trim to max size, keeping most recent
        if len(history) > MAX_COMMAND_HISTORY:
            history = history[-MAX_COMMAND_HISTORY:]

        self._data["command_history"] = history
        self._save()

    def clear_command_history(self) -> None:
        """Clear all command history."""
        self._data["command_history"] = []
        self._save()

    # -------------------------------------------------------------------------
    # Generic Getter/Setter Methods (for future features)
    # -------------------------------------------------------------------------

    def get(self, section: str, key: str, default: Any = None) -> Any:
        """Get a value from a storage section.

        Args:
            section: The storage section (e.g., "preferences", "cache")
            key: The key within the section
            default: Default value if key doesn't exist

        Returns:
            The stored value or default
        """
        section_data = self._data.get(section)
        if isinstance(section_data, dict):
            typed_section_data = cast(dict[str, Any], section_data)
            return typed_section_data.get(key, default)
        return default

    def set(self, section: str, key: str, value: Any) -> None:
        """Set a value in a storage section.

        Args:
            section: The storage section (e.g., "preferences", "cache")
            key: The key within the section
            value: The value to store (must be JSON-serializable)
        """
        if section not in self._data:
            self._data[section] = {}

        section_data = self._data[section]
        if isinstance(section_data, dict):
            typed_section_data = cast(dict[str, Any], section_data)
            typed_section_data[key] = value
            self._save()

    def delete(self, section: str, key: str) -> bool:
        """Delete a key from a storage section.

        Args:
            section: The storage section
            key: The key to delete

        Returns:
            True if key was deleted, False if it didn't exist
        """
        section_data = self._data.get(section)
        if isinstance(section_data, dict) and key in section_data:
            typed_section_data = cast(dict[str, Any], section_data)
            del typed_section_data[key]
            self._save()
            return True
        return False

    def get_section(self, section: str) -> dict[str, Any]:
        """Get an entire storage section.

        Args:
            section: The storage section name

        Returns:
            The section data as a dict, or empty dict if not found
        """
        section_data = self._data.get(section)
        if isinstance(section_data, dict):
            typed_section_data = cast(dict[str, Any], section_data)
            return dict(typed_section_data)
        return {}

    def clear_section(self, section: str) -> None:
        """Clear all data in a storage section.

        Args:
            section: The storage section to clear
        """
        if section in self._data:
            if section == "command_history":
                self._data[section] = []
            else:
                self._data[section] = {}
            self._save()


# Singleton instance for convenience
_storage: LocalStorage | None = None


def get_storage() -> LocalStorage:
    """Get the global LocalStorage instance.

    Returns:
        The singleton LocalStorage instance.
    """
    global _storage
    if _storage is None:
        _storage = LocalStorage()
    return _storage
