"""Directory and node management functionality for ArthHub MCP Server."""

# Import built-in modules
import asyncio
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


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


# Create Directory
def _create_directory_sync(storage: Storage, asset_hub: str, remote_dir_path: str) -> dict[str, Any]:
    """Create directory in ArthHub (synchronous).

    Args:
        storage: Storage instance
        asset_hub: Name of the asset repository
        remote_dir_path: Remote directory path to create

    Returns:
        dict: Creation result

    Raises:
        ArthubError: If creation fails

    """
    try:
        res = storage.create_directory_by_path(asset_hub, remote_dir_path)

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

        directory_id = res.data
        success_msg = f"Directory created successfully: {remote_dir_path}"
        logger.info(success_msg)

        return {
            "status": "success",
            "message": success_msg,
            "directory_id": directory_id,
            "asset_hub": asset_hub,
            "remote_path": remote_dir_path,
        }

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


@mcp.tool(name="create_directory")
async def create_directory_mcp(
    asset_hub: Annotated[str, Field(description="Name of the asset repository")],
    remote_dir_path: Annotated[str, Field(description="Remote directory path to create. Example: 'project/subdir'")],
) -> dict[str, Any]:
    """Create a directory in ArthHub storage.

    Args:
        asset_hub: Name of the asset repository
        remote_dir_path: Remote directory path to create

    Returns:
        dict: Response with creation result

    Raises:
        ArthubError: If creation fails

    """
    try:
        storage = await _get_storage()
        result = await asyncio.to_thread(_create_directory_sync, storage, asset_hub, remote_dir_path)
        return result
    except ArthubError:
        raise
    except Exception as e:
        error_msg = f"Error creating directory: {e!s}"
        logger.error(error_msg)
        raise ArthubError(error_msg, ErrorCode.UNKNOWN) from e


# Delete Node
def _delete_node_sync(storage: Storage, asset_hub: str, remote_node_path: str) -> dict[str, Any]:
    """Delete node (file or directory) from ArthHub (synchronous).

    Args:
        storage: Storage instance
        asset_hub: Name of the asset repository
        remote_node_path: Remote node path to delete

    Returns:
        dict: Deletion result

    Raises:
        ArthubError: If deletion fails

    """
    try:
        res = storage.delete_node_by_path(asset_hub, remote_node_path)

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

        node_id = res.data
        success_msg = f"Node deleted successfully: {remote_node_path}"
        logger.info(success_msg)

        return {
            "status": "success",
            "message": success_msg,
            "deleted_node_id": node_id,
            "asset_hub": asset_hub,
            "remote_path": remote_node_path,
        }

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


@mcp.tool(name="delete_node")
async def delete_node_mcp(
    asset_hub: Annotated[str, Field(description="Name of the asset repository")],
    remote_node_path: Annotated[
        str, Field(description="Remote node path to delete (can be file or directory). Example: 'project/file.jpg'")
    ],
) -> dict[str, Any]:
    """Delete a node (file or directory) from ArthHub storage.

    Args:
        asset_hub: Name of the asset repository
        remote_node_path: Remote node path to delete (file or directory)

    Returns:
        dict: Response with deletion result

    Raises:
        ArthubError: If deletion fails

    """
    try:
        storage = await _get_storage()
        result = await asyncio.to_thread(_delete_node_sync, storage, asset_hub, remote_node_path)
        return result
    except ArthubError:
        raise
    except Exception as e:
        error_msg = f"Error deleting node: {e!s}"
        logger.error(error_msg)
        raise ArthubError(error_msg, ErrorCode.UNKNOWN) from e


# Get Node Info
def _get_node_info_sync(storage: Storage, asset_hub: str, remote_node_path: str) -> dict[str, Any]:
    """Get node information from ArthHub (synchronous).

    Args:
        storage: Storage instance
        asset_hub: Name of the asset repository
        remote_node_path: Remote node path to query

    Returns:
        dict: Node information

    Raises:
        ArthubError: If query fails

    """
    try:
        res = storage.get_node_by_path(asset_hub, remote_node_path, simplified_meta=False)

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

        node_info = res.data
        success_msg = f"Node info retrieved successfully: {remote_node_path}"
        logger.info(success_msg)

        return {
            "status": "success",
            "message": success_msg,
            "node_info": node_info,
            "node_id": node_info.get("id"),
            "node_name": node_info.get("name"),
            "node_type": node_info.get("type"),
            "file_format": node_info.get("file_format"),
            "updated_date": node_info.get("updated_date"),
            "asset_hub": asset_hub,
            "remote_path": remote_node_path,
        }

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


@mcp.tool(name="get_node_info")
async def get_node_info_mcp(
    asset_hub: Annotated[str, Field(description="Name of the asset repository")],
    remote_node_path: Annotated[
        str, Field(description="Remote node path to query (can be file or directory). Example: 'project/file.jpg'")
    ],
) -> dict[str, Any]:
    """Get information about a node (file or directory) in ArthHub storage.

    Args:
        asset_hub: Name of the asset repository
        remote_node_path: Remote node path to query (file or directory)

    Returns:
        dict: Response with node information including id, name, type, format, etc.

    Raises:
        ArthubError: If query fails

    """
    try:
        storage = await _get_storage()
        result = await asyncio.to_thread(_get_node_info_sync, storage, asset_hub, remote_node_path)
        return result
    except ArthubError:
        raise
    except Exception as e:
        error_msg = f"Error getting node info: {e!s}"
        logger.error(error_msg)
        raise ArthubError(error_msg, ErrorCode.UNKNOWN) from e


# Get Child Nodes
def _get_child_nodes_sync(
    storage: Storage, asset_hub: str, remote_node_path: str, is_recursive: bool, simplified_meta: bool
) -> dict[str, Any]:
    """Get child nodes under a directory path (synchronous).

    Args:
        storage: Storage instance
        asset_hub: Name of the asset repository
        remote_node_path: Remote directory path to query
        is_recursive: Whether to query recursively
        simplified_meta: Whether to use simplified metadata

    Returns:
        dict: Child nodes information

    Raises:
        ArthubError: If query fails

    """
    try:
        # First get the parent node
        res = storage.get_node_by_path(asset_hub, remote_node_path, simplified_meta=True)

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

        parent_node = res.data

        # Check if it's a directory
        if not storage.is_node_directory(parent_node):
            error_msg = f"Node is not a directory: {remote_node_path}"
            logger.error(error_msg)
            raise ArthubError(error_msg, ErrorCode.INVALID_PARAMETER)

        # Get child nodes
        res = storage.get_child_nodes_brief(
            asset_hub=asset_hub,
            parent_node=parent_node,
            query_filters=[],
            is_recursive=is_recursive,
            simplified_meta=simplified_meta,
        )

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

        child_nodes = res.data
        success_msg = f"Retrieved {len(child_nodes)} child nodes from: {remote_node_path}"
        logger.info(success_msg)

        # Format child nodes information
        formatted_nodes = []
        for node in child_nodes:
            node_info = {
                "id": node.get("id"),
                "name": node.get("name"),
                "type": node.get("type"),
                "file_format": node.get("file_format"),
                "updated_date": node.get("updated_date"),
            }
            # Add full name if it's a file with extension
            if storage.is_node_file(node):
                node_info["full_name"] = storage.node_full_name(node)

            formatted_nodes.append(node_info)

        return {
            "status": "success",
            "message": success_msg,
            "parent_path": remote_node_path,
            "parent_id": parent_node.get("id"),
            "is_recursive": is_recursive,
            "child_count": len(child_nodes),
            "children": formatted_nodes,
            "asset_hub": asset_hub,
        }

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


@mcp.tool(name="get_child_nodes")
async def get_child_nodes_mcp(
    asset_hub: Annotated[str, Field(description="Name of the asset repository. Example: 'trial'")],
    remote_node_path: Annotated[
        str,
        Field(
            description="Remote directory path to query child nodes from. Example: 'project/images'. Use empty string '' for root directory"
        ),
    ],
    is_recursive: Annotated[
        bool,
        Field(
            description="Whether to query recursively and get all descendants (True) or only direct children (False). Default: False"
        ),
    ] = False,
    simplified_meta: Annotated[
        bool,
        Field(
            description="Whether to use simplified metadata for lower bandwidth consumption. Default: True"
        ),
    ] = True,
) -> dict[str, Any]:
    """Query child nodes (files and subdirectories) under a specified directory path in ArthHub.

    This tool allows you to explore the directory structure by retrieving all child nodes under a given path.
    You can use it to:
    - List all files and folders in a directory (non-recursive)
    - Recursively traverse the entire directory tree to get complete structure
    - Build a map of the repository contents

    Args:
        asset_hub: Name of the asset repository
        remote_node_path: Remote directory path to query. Use empty string for root
        is_recursive: If True, retrieves all descendants recursively; if False, only direct children
        simplified_meta: If True, uses simplified metadata to reduce bandwidth

    Returns:
        dict: Response containing:
            - status: Operation status
            - message: Description message
            - parent_path: The queried directory path
            - parent_id: ID of the parent directory
            - is_recursive: Whether recursive query was used
            - child_count: Total number of children found
            - children: List of child node information (id, name, type, file_format, etc.)
            - asset_hub: Asset repository name

    Raises:
        ArthubError: If the path doesn't exist, is not a directory, or query fails

    Examples:
        # Get direct children of a folder
        get_child_nodes(asset_hub="trial", remote_node_path="project/images", is_recursive=False)

        # Recursively get all descendants
        get_child_nodes(asset_hub="trial", remote_node_path="project", is_recursive=True)

    """
    try:
        storage = await _get_storage()
        result = await asyncio.to_thread(
            _get_child_nodes_sync, storage, asset_hub, remote_node_path, is_recursive, simplified_meta
        )
        return result
    except ArthubError:
        raise
    except Exception as e:
        error_msg = f"Error getting child nodes: {e!s}"
        logger.error(error_msg)
        raise ArthubError(error_msg, ErrorCode.UNKNOWN) from e





