import logging
import os
from typing import Annotated, Callable

import globus_sdk
from fastmcp import FastMCP
from fastmcp.exceptions import ToolError
from pydantic import Field

from .auth import get_globus_app, MockTransferClient
from .schemas import (
    TransferEndpoint,
    TransferEvent,
    TransferFile,
    TransferSubmitResponse,
)

logger = logging.getLogger(__name__)

mcp = FastMCP("Globus Transfer MCP Server")

# Check if running in test mode
TEST_MODE = os.getenv("MCP_TEST_MODE") == "1"


def get_transfer_client():
    app = get_globus_app()
    if TEST_MODE:
        return MockTransferClient(app=app)
    return globus_sdk.TransferClient(app=app)


def handle_gare(
    client_method: Callable[..., globus_sdk.GlobusHTTPResponse],
    *args,
    **kwargs,
) -> globus_sdk.GlobusHTTPResponse:
    if TEST_MODE:
        return client_method(*args, **kwargs)
    client: globus_sdk.TransferClient = client_method.__self__
    try:
        return client_method(*args, **kwargs)
    except globus_sdk.GlobusAPIError as e:
        if e.http_status == 403 and e.code == "ConsentRequired":
            scopes = e.info.consent_required.required_scopes
            for scope in scopes:
                client.add_app_scope(scope)
            return client_method(*args, **kwargs)
        raise


def _format_endpoint_search_response(
    res,
) -> list[TransferEndpoint]:
    endpoints = []
    data = res.data if hasattr(res, 'data') else res.get("DATA", [])
    for e in data:
        e: dict
        endpoint = TransferEndpoint(
            endpoint_id=e.get("id", "test-id"),
            display_name=e.get("display_name", "Test Endpoint"),
            owner_id=e.get("owner_id", "test-owner"),
            owner_string=e.get("owner_string", "Test Owner"),
            type=e.get("entity_type", "collection"),
            description=e.get("description", "Test Description"),
        )
        endpoints.append(endpoint)
    return endpoints


@mcp.tool
def search_endpoints_and_collections(
    search_filter: Annotated[
        str, Field(description="Required string to match endpoint fields against.")
    ],
    limit: Annotated[int, Field(description="Limit the number of results")],
) -> list[TransferEndpoint]:
    """Use a filter string to search all Globus Transfer endpoints and collections that
    are visible to the user.
    """
    tc = get_transfer_client()

    try:
        r = tc.endpoint_search(filter_fulltext=search_filter, limit=limit)
    except globus_sdk.GlobusAPIError as e:
        raise ToolError(f"Search failed: {e}")

    return _format_endpoint_search_response(r)


@mcp.tool
def list_my_endpoints_and_collections(
    limit: Annotated[int, Field(description="Limit the number of results")],
) -> list[TransferEndpoint]:
    """List Globus Transfer endpoints and collections for which the user has the
    'administrator' role
    """
    tc = get_transfer_client()

    try:
        r = tc.endpoint_search(filter_scope="administered-by-me", limit=limit)
    except globus_sdk.GlobusAPIError as e:
        raise ToolError(f"Search failed: {e}")

    return _format_endpoint_search_response(r)


@mcp.tool
def list_endpoints_and_collections_shared_with_me(
    limit: Annotated[int, Field(description="Limit the number of results")],
) -> list[TransferEndpoint]:
    """List shared Globus Transfer endpoints with permissions that give the user access."""
    tc = get_transfer_client()

    try:
        r = tc.endpoint_search(filter_scope="shared-with-me", limit=limit)
    except globus_sdk.GlobusAPIError as e:
        raise ToolError(f"Search failed: {e}")

    return _format_endpoint_search_response(r)


@mcp.tool
def submit_transfer_task(
    source_collection_id: Annotated[
        str, Field(description="ID of the source collection")
    ],
    destination_collection_id: Annotated[
        str, Field(description="ID of the destination collection")
    ],
    source_path: Annotated[
        str, Field(description="Path to the source directory or file of the transfer")
    ],
    destination_path: Annotated[
        str,
        Field(description="Path to the destination directory or file of the transfer"),
    ],
    label: Annotated[
        str, Field(default="MCP Transfer", description="Label for the transfer task")
    ],
) -> TransferSubmitResponse:
    """Submit a transfer task between two Globus Transfer collections.

    Use get_task_events to monitor the task's progress.
    """
    tc = get_transfer_client()

    data = globus_sdk.TransferData(
        source_endpoint=source_collection_id,
        destination_endpoint=destination_collection_id,
        label=label,
    )
    data.add_item(source_path=source_path, destination_path=destination_path)

    try:
        r = handle_gare(tc.submit_transfer, data)
    except globus_sdk.GlobusAPIError as e:
        raise ToolError(f"Failed to submit transfer: {e}")

    if TEST_MODE:
        return TransferSubmitResponse(task_id=r.data.get("task_id", "test-task-id"))
    return TransferSubmitResponse(task_id=r.data["task_id"])


@mcp.tool
def get_task_events(
    task_id: Annotated[str, Field(description="ID of the task")],
    limit: Annotated[int, Field(description="Limit the number of results")],
) -> list[TransferEvent]:
    """Get a list of task events to monitor the status and progress of a task.
    The events are ordered by time descending (newest first).
    """
    tc = get_transfer_client()

    try:
        r = tc.task_event_list(task_id=task_id, limit=limit)
    except globus_sdk.GlobusAPIError as e:
        raise ToolError(f"Failed to get task events: {e}")

    events = []
    data = r.data if hasattr(r, 'data') else r.get("DATA", [])
    for e in data:
        event = TransferEvent(
            code=e.get("code", "SUCCEEDED"),
            is_error=e.get("is_error", False),
            description=e.get("description", "Test event"),
            details=e.get("details", ""),
            time=e.get("time", "2024-01-01T00:00:00"),
        )
        events.append(event)

    return events


@mcp.tool
def list_directory(
    collection_id: Annotated[str, Field(description="ID of the collection")],
    path: Annotated[str, Field(description="Path to a directory")],
    limit: Annotated[int, Field(description="Limit the number of results")],
) -> list[TransferFile]:
    """List contents of a directory on a Globus Transfer collection"""
    tc = get_transfer_client()

    try:
        r = handle_gare(tc.operation_ls, collection_id, path=path, limit=limit)
    except globus_sdk.GlobusAPIError as e:
        raise ToolError(f"Failed to list directory contents: {e}")

    files = []
    data = r.data if hasattr(r, 'data') else r.get("DATA", [])
    for f in data:
        f: dict
        file = TransferFile(
            name=f.get("name", "test-file"),
            type=f.get("type", "file"),
            link_target=f.get("link_target"),
            user=f.get("user"),
            group=f.get("group"),
            permissions=f.get("permissions"),
            size=f.get("size", 0),
            last_modified=f.get("last_modified", "2024-01-01T00:00:00"),
        )
        files.append(file)

    return files


def main():
    """Main entry point for the Globus Transfer MCP Server."""
    mcp.run(transport="stdio")


if __name__ == "__main__":
    main()