# This is free software for the public good of a permacomputer hosted at
# permacomputer.com, an always-on computer by the people, for the people.
# One which is durable, easy to repair, & distributed like tap water
# for machine learning intelligence.
#
# The permacomputer is community-owned infrastructure optimized around
# four values:
#
#   TRUTH      First principles, math & science, open source code freely distributed
#   FREEDOM    Voluntary partnerships, freedom from tyranny & corporate control
#   HARMONY    Minimal waste, self-renewing systems with diverse thriving connections
#   LOVE       Be yourself without hurting others, cooperation through natural law
#
# This software contributes to that vision by archiving the web, preserving digital knowledge before it disappears.
# Code is seeds to sprout on any abandoned technology.

"""
AsyncFileVault 1.1.0 - Async wrapper for FileVault

Provides async versions of FileVault I/O operations using asyncio.to_thread().
CPU-bound operations (hash generation, path creation) remain synchronous.

Usage:
    vault = AsyncVault(vaultpath="vault", depth=9, use_pairs=True)

    # Sync operations (fast, no I/O)
    filename = vault.create_filename("seed", ".json", absolute=True)

    # Async operations (file I/O)
    await vault.atomic_write_json(filename, {"key": "value"})
    data = await vault.safe_read_json(filename)
"""

import asyncio
from functools import partial
from typing import Any, Optional, List

from .filevault import Vault, ensure_bytes, create_vault

__version__ = "1.1.0"
__author__ = "Russell Ballestrini"
__license__ = "Public Domain"


class AsyncVault(Vault):
    """
    Async wrapper for Vault with non-blocking file I/O.

    Inherits all sync methods from Vault but provides async versions
    of file operations that use asyncio.to_thread() to avoid blocking.

    Sync methods (instant, no await needed):
        - create_filename()
        - create_random_filename()
        - _generate_filename()
        - mark_file_exists()
        - clear_existence_cache()
        - get_cache_stats()
        - purge_cache_for_path_pattern()

    Async methods (await required):
        - atomic_write_json()
        - safe_read_json()
        - write_text_file()
        - read_text_file()
        - file_exists()
        - remove_file()
        - remove_file_if_exists()
    """

    def __init__(
        self,
        vaultpath: str = "vault",
        depth: int = 3,
        salt: str = "changeme",
        enable_memory_cache: bool = False,
        use_pairs: bool = False
    ):
        """
        Initialize AsyncVault with same parameters as Vault.

        Args:
            vaultpath: Base path for vault storage
            depth: Directory tree depth
            salt: Salt for hash generation
            enable_memory_cache: Enable in-memory file existence cache
            use_pairs: Use hex pairs for directories (256 dirs/level vs 16)
        """
        super().__init__(
            vaultpath=vaultpath,
            depth=depth,
            salt=salt,
            enable_memory_cache=enable_memory_cache,
            use_pairs=use_pairs
        )

    async def atomic_write_json(
        self,
        file_path: str,
        data: Any,
        indent: int = 2,
        aliases: Optional[List[str]] = None
    ) -> None:
        """
        Async version of atomic JSON write.

        Args:
            file_path: Target file path
            data: Data to write as JSON
            indent: JSON indentation level
            aliases: Optional list of alias keys for symlinks
        """
        await asyncio.to_thread(
            super().atomic_write_json,
            file_path,
            data,
            indent,
            aliases
        )

    async def safe_read_json(
        self,
        file_path: str,
        default: Any = None
    ) -> Any:
        """
        Async version of safe JSON read.

        Args:
            file_path: Path to JSON file
            default: Default value if file doesn't exist or is invalid

        Returns:
            Parsed JSON data or default value
        """
        return await asyncio.to_thread(
            super().safe_read_json,
            file_path,
            default
        )

    async def write_text_file(
        self,
        file_path: str,
        content: str,
        mode: str = "w"
    ) -> None:
        """
        Async version of text file write.

        Args:
            file_path: Target file path
            content: Text content to write
            mode: File open mode ('w', 'a', etc.)
        """
        await asyncio.to_thread(
            super().write_text_file,
            file_path,
            content,
            mode
        )

    async def read_text_file(
        self,
        file_path: str,
        default: Optional[str] = None
    ) -> str:
        """
        Async version of text file read.

        Args:
            file_path: Path to text file
            default: Default value if file doesn't exist

        Returns:
            File content or default value
        """
        return await asyncio.to_thread(
            super().read_text_file,
            file_path,
            default
        )

    async def file_exists(self, file_path: str) -> bool:
        """
        Async version of file existence check.

        Note: If memory cache is enabled and the path is cached,
        this returns immediately from cache without blocking.

        Args:
            file_path: Path to check

        Returns:
            True if file exists, False otherwise
        """
        # Check cache first (no I/O needed)
        if self.enable_memory_cache and self._existence_cache is not None:
            if file_path in self._existence_cache:
                return self._existence_cache[file_path]

        # Cache miss - need to hit filesystem
        return await asyncio.to_thread(super().file_exists, file_path)

    async def remove_file(self, file_path: str) -> bool:
        """
        Async version of file removal.

        Args:
            file_path: Path to file to remove

        Returns:
            True if file was removed, False if it didn't exist
        """
        # Call parent's sync remove_file directly via Vault class
        return await asyncio.to_thread(Vault.remove_file, self, file_path)

    async def remove_file_if_exists(self, file_path: str) -> bool:
        """
        Async version of remove_file_if_exists.

        Args:
            file_path: Path to file to remove

        Returns:
            True if file was removed, False if it didn't exist
        """
        try:
            return await asyncio.to_thread(Vault.remove_file, self, file_path)
        except OSError:
            self.mark_file_exists(file_path, False)
            return False


def create_async_vault(*args, **kwargs) -> AsyncVault:
    """Factory function for creating AsyncVault instances."""
    return AsyncVault(*args, **kwargs)
