"""File download functionality for ArthHub MCP Server."""

# Import built-in modules
import asyncio
from pathlib import Path
from typing import Annotated
from typing import Any

# Import third-party modules
from arthub_api import Storage
from loguru import logger
from mcp.server.fastmcp import Context
from pydantic import Field

# Import local modules
from arthub_mcp_server.app import mcp
from arthub_mcp_server.config import get_open_api
from arthub_mcp_server.errors import ArthubError
from arthub_mcp_server.errors import ErrorCode


async def download_file(
    asset_hub: str,
    remote_node_path: str,
    local_dir_path: str,
    same_name_override: bool = True,
    download_multi_version: bool = False,
    ctx: Context | None = None,
) -> dict[str, Any]:
    """Download file or directory from ArthHub storage.

    Args:
        asset_hub: Name of the asset repository
        remote_node_path: Remote node path to download (file or directory)
        local_dir_path: Local directory path to save the downloaded content
        same_name_override: Override existing file with same name
        download_multi_version: Download all versions of multi-version files
        ctx: FastMCP context

    Returns:
        dict: Response containing download result

    Raises:
        ArthubError: If download fails

    """
    if ctx:
        await ctx.report_progress(0.1)
        await ctx.info(f"Preparing to download: {remote_node_path}")

    try:
        # Validate local directory
        local_path = await _validate_local_dir(local_dir_path, ctx)

        # Get Storage instance
        if ctx:
            await ctx.report_progress(0.2)
            await ctx.info("Connecting to ArthHub...")

        storage = await _get_storage(ctx)

        # Download file or directory
        if ctx:
            await ctx.report_progress(0.3)
            await ctx.info(f"Downloading from {asset_hub}:{remote_node_path}...")

        result = await _download_from_arthub(
            storage=storage,
            asset_hub=asset_hub,
            remote_node_path=remote_node_path,
            local_dir_path=str(local_path),
            same_name_override=same_name_override,
            download_multi_version=download_multi_version,
            ctx=ctx,
        )

        return result

    except ArthubError:
        raise
    except Exception as e:
        error_msg = f"Error downloading file: {e!s}"
        logger.error(error_msg)
        if ctx:
            await ctx.error(error_msg)
        raise ArthubError(error_msg, ErrorCode.UNKNOWN) from e


def _validate_local_dir_sync(local_dir_path: str | Path) -> Path:
    """Validate local directory path (synchronous).

    Args:
        local_dir_path: Path to local directory

    Returns:
        Path: Validated directory path

    Raises:
        ArthubError: If directory creation fails

    """
    # Convert to Path object if string
    if isinstance(local_dir_path, str):
        local_dir_path = Path(local_dir_path)

    # Create directory if not exists
    try:
        local_dir_path.mkdir(parents=True, exist_ok=True)
    except Exception as e:
        error_msg = f"Failed to create local directory: {local_dir_path}, error: {e!s}"
        logger.error(error_msg)
        raise ArthubError(error_msg, ErrorCode.FILE_ERROR) from e

    logger.info(f"Local directory ready: {local_dir_path}")
    return local_dir_path


async def _validate_local_dir(local_dir_path: str | Path, ctx: Context | None = None) -> Path:
    """Validate local directory path.

    Args:
        local_dir_path: Path to local directory
        ctx: FastMCP context

    Returns:
        Path: Validated directory path

    Raises:
        ArthubError: If directory creation fails

    """
    try:
        return await asyncio.to_thread(_validate_local_dir_sync, local_dir_path)
    except ArthubError as e:
        if ctx:
            await ctx.error(str(e))
        raise


def _get_storage_sync() -> Storage:
    """Get Storage instance with authentication (synchronous).

    Returns:
        Storage: Authenticated Storage instance

    Raises:
        ArthubError: If authentication fails

    """
    try:
        open_api = get_open_api()
        storage = Storage(open_api)
        logger.info("Storage instance created")
        return storage
    except ArthubError:
        raise
    except Exception as e:
        error_msg = f"Failed to create Storage instance: {e!s}"
        logger.error(error_msg)
        raise ArthubError(error_msg, ErrorCode.CONFIG_ERROR) from e


async def _get_storage(ctx: Context | None = None) -> Storage:
    """Get Storage instance with authentication.

    Args:
        ctx: FastMCP context

    Returns:
        Storage: Authenticated Storage instance

    Raises:
        ArthubError: If authentication fails

    """
    try:
        return await asyncio.to_thread(_get_storage_sync)
    except ArthubError as e:
        if ctx:
            await ctx.error(str(e))
        raise


def _download_from_arthub_sync(
    storage: Storage,
    asset_hub: str,
    remote_node_path: str,
    local_dir_path: str,
    same_name_override: bool,
    download_multi_version: bool,
) -> dict[str, Any]:
    """Download file or directory from ArthHub using Storage API (synchronous).

    Args:
        storage: Storage instance
        asset_hub: Name of the asset repository
        remote_node_path: Remote node path
        local_dir_path: Local directory path
        same_name_override: Override existing file
        download_multi_version: Download all versions

    Returns:
        dict: Download result

    Raises:
        ArthubError: If download fails

    """
    # Progress callback
    def progress_cb(completed: int, total: int) -> None:
        percentage = (float(completed) / float(total)) * 100.0
        logger.info(f"Download progress: {percentage:.1f}%")

    try:
        # Call ArthHub API (synchronous)
        res = storage.download_by_path(
            asset_hub=asset_hub,
            remote_node_path=remote_node_path,
            local_dir_path=local_dir_path,
            same_name_override=same_name_override,
            progress_cb=progress_cb,
            download_multi_version=download_multi_version,
        )

        if not res.is_succeeded():
            error_msg = f"Download failed: {res.error_message()}"
            logger.error(error_msg)
            raise ArthubError(error_msg, ErrorCode.API_FAILURE)

        # Parse result
        downloaded_paths = res.data
        root_path = downloaded_paths[0] if downloaded_paths else local_dir_path

        success_msg = f"Download successful: {remote_node_path}"
        logger.info(success_msg)

        return {
            "status": "success",
            "message": success_msg,
            "local_path": root_path,
            "downloaded_paths": downloaded_paths,
            "asset_hub": asset_hub,
            "remote_path": remote_node_path,
        }

    except ArthubError:
        raise
    except Exception as e:
        error_msg = f"Download failed: {e!s}"
        logger.error(error_msg)
        raise ArthubError(error_msg, ErrorCode.API_FAILURE) from e


async def _download_from_arthub(
    storage: Storage,
    asset_hub: str,
    remote_node_path: str,
    local_dir_path: str,
    same_name_override: bool,
    download_multi_version: bool,
    ctx: Context | None = None,
) -> dict[str, Any]:
    """Download file or directory from ArthHub using Storage API.

    Args:
        storage: Storage instance
        asset_hub: Name of the asset repository
        remote_node_path: Remote node path
        local_dir_path: Local directory path
        same_name_override: Override existing file
        download_multi_version: Download all versions
        ctx: FastMCP context

    Returns:
        dict: Download result

    Raises:
        ArthubError: If download fails

    """
    try:
        result = await asyncio.to_thread(
            _download_from_arthub_sync,
            storage,
            asset_hub,
            remote_node_path,
            local_dir_path,
            same_name_override,
            download_multi_version,
        )

        if ctx:
            await ctx.report_progress(1.0)
            await ctx.info(result["message"])

        return result

    except ArthubError as e:
        if ctx:
            await ctx.error(str(e))
        raise


@mcp.tool(name="download_file")
async def download_file_mcp(
    asset_hub: Annotated[str, Field(description="Name of the asset repository")],
    remote_node_path: Annotated[str, Field(description="Remote node path to download (file or directory)")],
    local_dir_path: Annotated[str, Field(description="Local directory path to save the downloaded content")],
    same_name_override: Annotated[
        bool,
        Field(description="Override existing file with same name, defaults to True"),
    ] = True,
    download_multi_version: Annotated[
        bool,
        Field(description="Download all versions of multi-version files, defaults to False"),
    ] = False,
) -> dict[str, Any]:
    """Download file or directory from ArthHub storage.

    Args:
        asset_hub: Name of the asset repository
        remote_node_path: Remote node path to download (file or directory)
        local_dir_path: Local directory path to save the downloaded content
        same_name_override: Override existing file with same name
        download_multi_version: Download all versions of multi-version files

    Returns:
        dict: Response with download result

    Raises:
        ArthubError: If download fails

    """
    return await download_file(
        asset_hub=asset_hub,
        remote_node_path=remote_node_path,
        local_dir_path=local_dir_path,
        same_name_override=same_name_override,
        download_multi_version=download_multi_version,
        ctx=None,
    )





