# Copyright (c) CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com
#

"""
Secure file-based token storage for CloudZero Operations CLI.

This module provides secure file storage for authentication tokens and user information
using proper file permissions (0600) in the ~/.cloudzero/ directory.
"""

import json
import logging
import os
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)


class TokenStorageError(Exception):
    """Base exception for token storage operations."""

    pass


class TokenStorage:
    """
    Secure file-based token storage system.

    Stores authentication tokens and user information in JSON files with secure
    permissions (0600) in the ~/.cloudzero/ directory.
    """

    def __init__(self, storage_dir: Path | None = None):
        """
        Initialize token storage.

        Args:
            storage_dir: Custom storage directory. Defaults to ~/.cloudzero/
        """
        self.storage_dir = storage_dir or Path.home() / ".cloudzero"
        self.token_file = self.storage_dir / "auth_token.json"
        self.userinfo_file = self.storage_dir / "userinfo.json"

        # Ensure storage directory exists with secure permissions
        self._ensure_storage_dir()

    def _ensure_storage_dir(self) -> None:
        """Create storage directory with secure permissions if it doesn't exist."""
        try:
            self.storage_dir.mkdir(mode=0o700, exist_ok=True)
            # Ensure permissions are correct even if directory already existed
            os.chmod(self.storage_dir, 0o700)
        except OSError as e:
            raise TokenStorageError(f"Failed to create storage directory {self.storage_dir}: {e}") from e

    def store_token(self, token: dict[str, Any]) -> None:
        """
        Store authentication token securely.

        Args:
            token: Token dictionary containing access_token, refresh_token, etc.

        Raises:
            TokenStorageError: If token storage fails
        """
        if not token:
            raise TokenStorageError("Token cannot be empty")

        try:
            # Write token to file with secure permissions
            self._write_secure_json(self.token_file, token)
            logger.debug(f"Token stored successfully in {self.token_file}")
        except Exception as e:
            raise TokenStorageError(f"Failed to store token: {e}") from e

    def get_stored_token(self) -> dict[str, Any] | None:
        """
        Retrieve stored authentication token.

        Returns:
            Token dictionary or None if not found

        Raises:
            TokenStorageError: If token retrieval fails
        """
        try:
            return self._read_secure_json(self.token_file)
        except Exception as e:
            raise TokenStorageError(f"Failed to retrieve token: {e}") from e

    def store_userinfo(self, userinfo: dict[str, Any]) -> None:
        """
        Store user information securely.

        Args:
            userinfo: User information dictionary

        Raises:
            TokenStorageError: If userinfo storage fails
        """
        if not userinfo:
            raise TokenStorageError("Userinfo cannot be empty")

        try:
            self._write_secure_json(self.userinfo_file, userinfo)
            logger.debug(f"Userinfo stored successfully in {self.userinfo_file}")
        except Exception as e:
            raise TokenStorageError(f"Failed to store userinfo: {e}") from e

    def get_stored_userinfo(self) -> dict[str, Any] | None:
        """
        Retrieve stored user information.

        Returns:
            Userinfo dictionary or None if not found

        Raises:
            TokenStorageError: If userinfo retrieval fails
        """
        try:
            return self._read_secure_json(self.userinfo_file)
        except Exception as e:
            raise TokenStorageError(f"Failed to retrieve userinfo: {e}") from e

    def clear_all(self) -> None:
        """
        Clear all stored authentication data.

        Raises:
            TokenStorageError: If cleanup fails
        """
        errors = []

        for file_path in [self.token_file, self.userinfo_file]:
            try:
                if file_path.exists():
                    file_path.unlink()
                    logger.debug(f"Removed {file_path}")
            except OSError as e:
                errors.append(f"Failed to remove {file_path}: {e}")

        if errors:
            raise TokenStorageError(f"Cleanup failed: {'; '.join(errors)}")

    def has_token(self) -> bool:
        """
        Check if a token file exists.

        Returns:
            True if token file exists, False otherwise
        """
        return self.token_file.exists()

    def has_userinfo(self) -> bool:
        """
        Check if a userinfo file exists.

        Returns:
            True if userinfo file exists, False otherwise
        """
        return self.userinfo_file.exists()

    def _write_secure_json(self, file_path: Path, data: dict[str, Any]) -> None:
        """
        Write JSON data to file with secure permissions.

        Args:
            file_path: Path to write to
            data: Data to write as JSON

        Raises:
            OSError: If file operations fail
        """
        # Create parent directory if it doesn't exist
        file_path.parent.mkdir(mode=0o700, exist_ok=True)

        # Write data to file
        with open(file_path, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2)

        # Set secure permissions (owner read/write only)
        os.chmod(file_path, 0o600)

    def _read_secure_json(self, file_path: Path) -> dict[str, Any] | None:
        """
        Read JSON data from file with security checks.

        Args:
            file_path: Path to read from

        Returns:
            Parsed JSON data or None if file doesn't exist

        Raises:
            OSError: If file operations fail
            json.JSONDecodeError: If JSON parsing fails
        """
        if not file_path.exists():
            return None

        # Check file permissions for security
        file_stat = file_path.stat()
        file_mode = file_stat.st_mode & 0o777

        if file_mode != 0o600:
            logger.warning(f"Insecure file permissions on {file_path}: {oct(file_mode)}. Fixing...")
            os.chmod(file_path, 0o600)

        # Read and parse JSON
        with open(file_path, encoding="utf-8") as f:
            return json.load(f)


def get_token_storage() -> TokenStorage:
    """
    Get a TokenStorage instance.

    This factory function allows for easier testing and dependency injection.

    Returns:
        TokenStorage instance
    """
    return TokenStorage()
