"""Persistent state storage for agent data."""

from __future__ import annotations

import json
import stat
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

import structlog

from curadise_agent.errors import StateError

if TYPE_CHECKING:
    from pathlib import Path

log = structlog.get_logger(__name__)

# Secure file permissions (owner read/write only)
SECURE_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR  # 0o600


@dataclass
class StateStorage:
    """
    Handles persistent storage of agent state.

    Manages agent ID, authentication tokens, and other persistent data
    with appropriate file permissions for security.
    """

    directory: Path

    def __post_init__(self) -> None:
        """Ensure storage directory exists with proper permissions."""
        self._ensure_directory()

    def _ensure_directory(self) -> None:
        """Create storage directory with secure permissions."""
        try:
            self.directory.mkdir(parents=True, exist_ok=True)
            # Set directory permissions to 0o700 (owner only)
            self.directory.chmod(stat.S_IRWXU)
        except Exception as e:
            raise StateError(f"Failed to create state directory: {e}", cause=e) from e

    def _get_path(self, filename: str) -> Path:
        """Get full path for a storage file."""
        return self.directory / filename

    def _read_file(self, path: Path) -> str | None:
        """Read content from a file."""
        if not path.exists():
            return None
        try:
            return path.read_text(encoding="utf-8").strip()
        except Exception as e:
            log.error("state_read_failed", path=str(path), error=str(e))
            return None

    def _write_file(self, path: Path, content: str, secure: bool = True) -> None:
        """Write content to a file with optional secure permissions."""
        try:
            # Write to temp file first
            temp_path = path.with_suffix(".tmp")
            temp_path.write_text(content, encoding="utf-8")

            if secure:
                temp_path.chmod(SECURE_FILE_MODE)

            # Atomic rename
            temp_path.rename(path)
            log.debug("state_written", path=str(path))
        except Exception as e:
            raise StateError(f"Failed to write state file: {e}", cause=e) from e

    def _read_json(self, path: Path) -> dict[str, Any] | None:
        """Read JSON content from a file."""
        content = self._read_file(path)
        if content is None:
            return None
        try:
            result: dict[str, Any] = json.loads(content)
            return result
        except json.JSONDecodeError as e:
            log.error("state_json_parse_failed", path=str(path), error=str(e))
            return None

    def _write_json(self, path: Path, data: dict[str, Any], secure: bool = True) -> None:
        """Write JSON content to a file."""
        content = json.dumps(data, indent=2)
        self._write_file(path, content, secure=secure)

    # Agent ID operations

    def get_agent_id(self, filename: str = "agent_id") -> str | None:
        """
        Retrieve stored agent ID.

        Args:
            filename: Name of the agent ID file

        Returns:
            Agent ID string or None if not stored
        """
        return self._read_file(self._get_path(filename))

    def save_agent_id(self, agent_id: str, filename: str = "agent_id") -> None:
        """
        Save agent ID to storage.

        Args:
            agent_id: Agent ID to save
            filename: Name of the agent ID file
        """
        self._write_file(self._get_path(filename), agent_id, secure=True)
        log.info("agent_id_saved", agent_id=agent_id)

    def delete_agent_id(self, filename: str = "agent_id") -> bool:
        """
        Delete stored agent ID.

        Args:
            filename: Name of the agent ID file

        Returns:
            True if deleted, False if not found
        """
        path = self._get_path(filename)
        if path.exists():
            path.unlink()
            log.info("agent_id_deleted")
            return True
        return False

    # Token operations

    def get_token(self, filename: str = "token") -> str | None:
        """
        Retrieve stored authentication token.

        Args:
            filename: Name of the token file

        Returns:
            Token string or None if not stored
        """
        return self._read_file(self._get_path(filename))

    def save_token(self, token: str, filename: str = "token") -> None:
        """
        Save authentication token to storage.

        Args:
            token: Token to save
            filename: Name of the token file
        """
        self._write_file(self._get_path(filename), token, secure=True)
        log.info("token_saved")

    def delete_token(self, filename: str = "token") -> bool:
        """
        Delete stored token.

        Args:
            filename: Name of the token file

        Returns:
            True if deleted, False if not found
        """
        path = self._get_path(filename)
        if path.exists():
            path.unlink()
            log.info("token_deleted")
            return True
        return False

    # Generic key-value operations

    def get(self, key: str) -> dict[str, Any] | None:
        """
        Get a JSON value by key.

        Args:
            key: Storage key (used as filename with .json extension)

        Returns:
            Stored data or None
        """
        return self._read_json(self._get_path(f"{key}.json"))

    def set(self, key: str, data: dict[str, Any], secure: bool = False) -> None:
        """
        Set a JSON value by key.

        Args:
            key: Storage key
            data: Data to store
            secure: Whether to use secure file permissions
        """
        self._write_json(self._get_path(f"{key}.json"), data, secure=secure)

    def delete(self, key: str) -> bool:
        """
        Delete a stored value.

        Args:
            key: Storage key

        Returns:
            True if deleted, False if not found
        """
        path = self._get_path(f"{key}.json")
        if path.exists():
            path.unlink()
            return True
        return False

    def exists(self, key: str) -> bool:
        """Check if a key exists in storage."""
        return self._get_path(f"{key}.json").exists()

    def list_keys(self) -> list[str]:
        """List all stored keys."""
        keys = []
        for path in self.directory.glob("*.json"):
            keys.append(path.stem)
        return sorted(keys)

    def clear_all(self) -> int:
        """
        Delete all stored data.

        Returns:
            Number of files deleted
        """
        count = 0
        for path in self.directory.iterdir():
            if path.is_file():
                path.unlink()
                count += 1
        log.warning("state_cleared", deleted_count=count)
        return count
