#!/usr/bin/env python3
"""
Simple Trello MCP Server using FastMCP

Setup with uv:
    uv init
    uv add fastmcp requests

Run:
    uv run mcp_client.py
"""

import os
from typing import Any
from urllib.parse import quote
import requests
from fastmcp import FastMCP

# Initialize FastMCP
mcp = FastMCP("Trello")

# Get credentials from environment
API_KEY = os.getenv("TRELLO_API_KEY")
API_TOKEN = os.getenv("TRELLO_API_TOKEN")
BASE_URL = "https://api.trello.com/1"


def trello_request(endpoint: str, method: str = "GET", data: dict | None = None) -> Any:
    """Make a request to Trello API"""
    url = f"{BASE_URL}{endpoint}"
    params = {"key": API_KEY, "token": API_TOKEN}

    if method == "GET":
        response = requests.get(url, params=params)
    elif method == "POST":
        response = requests.post(url, params=params, json=data)
    elif method == "PUT":
        response = requests.put(url, params=params, json=data)
    elif method == "DELETE":
        response = requests.delete(url, params=params)
    else:
        raise ValueError(f"Unsupported HTTP method: {method}")

    response.raise_for_status()
    return response.json()


# ============== HELPERS ==============


def should_optimize() -> bool:
    """Check if response optimization is enabled"""
    return os.getenv("TRELLO_OPTIMIZE_RESPONSE", "0") == "1"


def simplify_label(label: dict) -> dict:
    """Simplify a label object"""
    return {
        "id": label.get("id"),
        "name": label.get("name"),
        "color": label.get("color"),
    }


def simplify_card(card: dict) -> dict:
    """Simplify a card object"""
    simplified = {
        "id": card.get("id"),
        "name": card.get("name"),
        "url": card.get("url"),
        "list_id": card.get("idList"),
        "board_id": card.get("idBoard"),
    }

    if card.get("desc"):
        simplified["desc"] = card.get("desc")

    if card.get("due"):
        simplified["due"] = card.get("due")

    if card.get("start"):
        simplified["start"] = card.get("start")

    if card.get("dueComplete") is not None:
        simplified["due_complete"] = card.get("dueComplete")

    if card.get("labels"):
        simplified["labels"] = [simplify_label(l) for l in card.get("labels", [])]

    if card.get("idMembers"):
        simplified["member_ids"] = card.get("idMembers")

    return simplified


def simplify_list(list_obj: dict) -> dict:
    """Simplify a list object"""
    return {
        "id": list_obj.get("id"),
        "name": list_obj.get("name"),
        "board_id": list_obj.get("idBoard"),
    }


def simplify_board(board: dict) -> dict:
    """Simplify a board object"""
    return {
        "id": board.get("id"),
        "name": board.get("name"),
        "desc": board.get("desc", ""),
        "url": board.get("url"),
        "short_url": board.get("shortUrl"),
    }


def simplify_check_item(item: dict) -> dict:
    """Simplify a checklist item object"""
    return {
        "id": item.get("id"),
        "name": item.get("name"),
        "state": item.get("state"),
        "pos": item.get("pos"),
    }


def simplify_checklist(checklist: dict) -> dict:
    """Simplify a checklist object"""
    simplified = {
        "id": checklist.get("id"),
        "name": checklist.get("name"),
        "card_id": checklist.get("idCard"),
        "pos": checklist.get("pos"),
    }
    if checklist.get("checkItems"):
        simplified["check_items"] = [
            simplify_check_item(i) for i in checklist.get("checkItems", [])
        ]
    return simplified


def simplify_custom_field(field: dict) -> dict:
    """Simplify a custom field definition"""
    simplified = {
        "id": field.get("id"),
        "name": field.get("name"),
        "type": field.get("type"),
        "board_id": field.get("idModel"),
    }
    if field.get("options"):
        simplified["options"] = [
            {"id": o.get("id"), "value": o.get("value"), "color": o.get("color"), "pos": o.get("pos")}
            for o in field.get("options", [])
        ]
    return simplified


def simplify_custom_field_item(item: dict) -> dict:
    """Simplify a custom field value on a card"""
    return {
        "id": item.get("id"),
        "custom_field_id": item.get("idCustomField"),
        "value": item.get("value"),
        "card_id": item.get("idModel"),
    }


def simplify_search_results(results: dict) -> dict:
    """Simplify search results"""
    simplified = {}
    if results.get("cards"):
        simplified["cards"] = [simplify_card(c) for c in results["cards"]]
    if results.get("boards"):
        simplified["boards"] = [simplify_board(b) for b in results["boards"]]
    return simplified


# ============== BOARDS ==============


@mcp.tool()
def get_my_boards() -> list[dict]:
    """Get all boards for the authenticated user"""
    boards = trello_request("/members/me/boards")
    if should_optimize():
        return [simplify_board(b) for b in boards]
    return boards


@mcp.tool()
def get_board(board_id: str) -> dict:
    """Get details of a specific board"""
    board = trello_request(f"/boards/{board_id}")
    if should_optimize():
        return simplify_board(board)
    return board


@mcp.tool()
def create_board(name: str, desc: str = "") -> dict:
    """Create a new Trello board"""
    board = trello_request("/boards", "POST", {"name": name, "desc": desc})
    if should_optimize():
        return simplify_board(board)
    return board


# ============== LISTS ==============


@mcp.tool()
def get_board_lists(board_id: str) -> list[dict]:
    """Get all lists on a board"""
    lists = trello_request(f"/boards/{board_id}/lists")
    if should_optimize():
        return [simplify_list(l) for l in lists]
    return lists


@mcp.tool()
def create_list(board_id: str, name: str) -> dict:
    """Create a new list on a board"""
    list_obj = trello_request("/lists", "POST", {"idBoard": board_id, "name": name})
    if should_optimize():
        return simplify_list(list_obj)
    return list_obj


@mcp.tool()
def archive_list(list_id: str) -> dict:
    """Archive a list"""
    list_obj = trello_request(f"/lists/{list_id}/closed", "PUT", {"value": True})
    if should_optimize():
        return simplify_list(list_obj)
    return list_obj


@mcp.tool()
def archive_all_cards_in_list(list_id: str) -> dict:
    """Archive all cards in a list"""
    return trello_request(f"/lists/{list_id}/archiveAllCards", "POST")


@mcp.tool()
def move_all_cards_in_list(list_id: str, target_list_id: str, target_board_id: str | None = None) -> dict:
    """Move all cards in a list to another list"""
    data = {"idList": target_list_id}
    if target_board_id:
        data["idBoard"] = target_board_id
    return trello_request(f"/lists/{list_id}/moveAllCards", "POST", data)


# ============== CARDS ==============


@mcp.tool()
def get_board_cards(board_id: str) -> list[dict]:
    """Get all cards on a board"""
    cards = trello_request(f"/boards/{board_id}/cards")
    if should_optimize():
        return [simplify_card(c) for c in cards]
    return cards


@mcp.tool()
def get_list_cards(list_id: str) -> list[dict]:
    """Get all cards in a list"""
    cards = trello_request(f"/lists/{list_id}/cards")
    if should_optimize():
        return [simplify_card(c) for c in cards]
    return cards


@mcp.tool()
def get_card(card_id: str) -> dict:
    """Get details of a specific card"""
    card = trello_request(f"/cards/{card_id}")
    if should_optimize():
        return simplify_card(card)
    return card


@mcp.tool()
def create_card(list_id: str, name: str, desc: str = "") -> dict:
    """Create a new card in a list"""
    card = trello_request(
        "/cards", "POST", {"idList": list_id, "name": name, "desc": desc}
    )
    if should_optimize():
        return simplify_card(card)
    return card


@mcp.tool()
def update_card(
    card_id: str,
    name: str | None = None,
    desc: str | None = None,
    due: str | None = None,
    start: str | None = None,
    due_complete: bool | None = None,
) -> dict:
    """Update a card's name, description, or dates"""
    data = {}
    if name is not None:
        data["name"] = name
    if desc is not None:
        data["desc"] = desc
    if due is not None:
        data["due"] = due
    if start is not None:
        data["start"] = start
    if due_complete is not None:
        data["dueComplete"] = due_complete
    card = trello_request(f"/cards/{card_id}", "PUT", data)
    if should_optimize():
        return simplify_card(card)
    return card


@mcp.tool()
def archive_card(card_id: str) -> dict:
    """Archive a card"""
    card = trello_request(f"/cards/{card_id}", "PUT", {"closed": True})
    if should_optimize():
        return simplify_card(card)
    return card


@mcp.tool()
def delete_card(card_id: str) -> dict:
    """Permanently delete a card"""
    return trello_request(f"/cards/{card_id}", "DELETE")


@mcp.tool()
def move_card(card_id: str, list_id: str) -> dict:
    """Move a card to a different list"""
    card = trello_request(f"/cards/{card_id}", "PUT", {"idList": list_id})
    if should_optimize():
        return simplify_card(card)
    return card


# ============== ACTIONS ==============


@mcp.tool()
def get_board_actions(board_id: str, limit: int = 50) -> list[dict]:
    """Get recent actions (activity) on a board"""
    # Actions are complex and variable, leaving as-is for now or could simplify later
    # The user request focused on lists and cards mostly.
    return trello_request(f"/boards/{board_id}/actions?limit={limit}")


@mcp.tool()
def get_card_actions(card_id: str, limit: int = 50) -> list[dict]:
    """Get actions (activity/comments) on a card"""
    return trello_request(f"/cards/{card_id}/actions?limit={limit}")


@mcp.tool()
def get_action(action_id: str) -> dict:
    """Get details of a specific action"""
    return trello_request(f"/actions/{action_id}")


@mcp.tool()
def update_comment(action_id: str, text: str) -> dict:
    """Update a comment (action must be a comment type)"""
    return trello_request(f"/actions/{action_id}", "PUT", {"text": text})


@mcp.tool()
def delete_comment(action_id: str) -> dict:
    """Delete a comment (action must be a comment type)"""
    return trello_request(f"/actions/{action_id}", "DELETE")


# ============== COMMENTS ==============


@mcp.tool()
def add_comment(card_id: str, text: str) -> dict:
    """Add a comment to a card"""
    return trello_request(f"/cards/{card_id}/actions/comments", "POST", {"text": text})


# ============== MEMBERS ==============


@mcp.tool()
def get_me() -> dict:
    """Get information about the authenticated user"""
    return trello_request("/members/me")


# ============== CHECKLISTS ==============


@mcp.tool()
def create_checklist(card_id: str, name: str) -> dict:
    """Create a checklist on a card"""
    checklist = trello_request("/checklists", "POST", {"idCard": card_id, "name": name})
    if should_optimize():
        return simplify_checklist(checklist)
    return checklist


@mcp.tool()
def get_card_checklists(card_id: str) -> list[dict]:
    """Get all checklists on a card"""
    checklists = trello_request(f"/cards/{card_id}/checklists")
    if should_optimize():
        return [simplify_checklist(c) for c in checklists]
    return checklists


@mcp.tool()
def delete_checklist(checklist_id: str) -> dict:
    """Delete a checklist"""
    return trello_request(f"/checklists/{checklist_id}", "DELETE")


@mcp.tool()
def add_checklist_item(checklist_id: str, name: str, checked: bool = False) -> dict:
    """Add an item to a checklist"""
    item = trello_request(
        f"/checklists/{checklist_id}/checkItems", "POST",
        {"name": name, "checked": str(checked).lower()},
    )
    if should_optimize():
        return simplify_check_item(item)
    return item


@mcp.tool()
def update_checklist_item(
    card_id: str, check_item_id: str, name: str | None = None, state: str | None = None
) -> dict:
    """Update a checklist item's name or state"""
    data = {}
    if name is not None:
        data["name"] = name
    if state is not None:
        data["state"] = state
    item = trello_request(f"/cards/{card_id}/checkItem/{check_item_id}", "PUT", data)
    if should_optimize():
        return simplify_check_item(item)
    return item


@mcp.tool()
def delete_checklist_item(checklist_id: str, check_item_id: str) -> dict:
    """Delete a checklist item"""
    return trello_request(f"/checklists/{checklist_id}/checkItems/{check_item_id}", "DELETE")


# ============== LABELS ==============


@mcp.tool()
def get_board_labels(board_id: str) -> list[dict]:
    """Get all labels on a board"""
    labels = trello_request(f"/boards/{board_id}/labels")
    if should_optimize():
        return [simplify_label(l) for l in labels]
    return labels


@mcp.tool()
def create_label(board_id: str, name: str, color: str | None = None) -> dict:
    """Create a label on a board"""
    data = {"idBoard": board_id, "name": name}
    if color:
        data["color"] = color
    label = trello_request("/labels", "POST", data)
    if should_optimize():
        return simplify_label(label)
    return label


@mcp.tool()
def update_label(label_id: str, name: str | None = None, color: str | None = None) -> dict:
    """Update a label's name or color"""
    data = {}
    if name is not None:
        data["name"] = name
    if color is not None:
        data["color"] = color
    label = trello_request(f"/labels/{label_id}", "PUT", data)
    if should_optimize():
        return simplify_label(label)
    return label


@mcp.tool()
def delete_label(label_id: str) -> dict:
    """Delete a label"""
    return trello_request(f"/labels/{label_id}", "DELETE")


@mcp.tool()
def add_label_to_card(card_id: str, label_id: str) -> dict:
    """Add an existing label to a card"""
    return trello_request(f"/cards/{card_id}/idLabels", "POST", {"value": label_id})


@mcp.tool()
def remove_label_from_card(card_id: str, label_id: str) -> dict:
    """Remove a label from a card"""
    return trello_request(f"/cards/{card_id}/idLabels/{label_id}", "DELETE")


# ============== CARD MEMBERS ==============


@mcp.tool()
def add_member_to_card(card_id: str, member_id: str) -> dict:
    """Assign a member to a card"""
    return trello_request(f"/cards/{card_id}/idMembers", "POST", {"value": member_id})


@mcp.tool()
def remove_member_from_card(card_id: str, member_id: str) -> dict:
    """Remove a member from a card"""
    return trello_request(f"/cards/{card_id}/idMembers/{member_id}", "DELETE")


# ============== CUSTOM FIELDS ==============


@mcp.tool()
def get_board_custom_fields(board_id: str) -> list[dict]:
    """Get all custom field definitions on a board"""
    fields = trello_request(f"/boards/{board_id}/customFields")
    if should_optimize():
        return [simplify_custom_field(f) for f in fields]
    return fields


@mcp.tool()
def get_card_custom_field_items(card_id: str) -> list[dict]:
    """Get all custom field values on a card"""
    items = trello_request(f"/cards/{card_id}/customFieldItems")
    if should_optimize():
        return [simplify_custom_field_item(i) for i in items]
    return items


@mcp.tool()
def update_card_custom_field_item(card_id: str, custom_field_id: str, value: dict) -> dict:
    """Set a custom field value on a card"""
    item = trello_request(
        f"/cards/{card_id}/customField/{custom_field_id}/item", "PUT", {"value": value}
    )
    if should_optimize():
        return simplify_custom_field_item(item)
    return item


# ============== SEARCH ==============


@mcp.tool()
def search(
    query: str,
    model_types: str = "cards",
    cards_limit: int = 10,
    boards_limit: int = 10,
    board_ids: str | None = None,
    partial: bool = False,
) -> dict:
    """Search for cards, boards, and other Trello objects"""
    endpoint = f"/search?query={quote(query)}&modelTypes={model_types}&cards_limit={cards_limit}&boards_limit={boards_limit}"
    if board_ids:
        endpoint += f"&idBoards={quote(board_ids)}"
    if partial:
        endpoint += "&partial=true"
    results = trello_request(endpoint)
    if should_optimize():
        return simplify_search_results(results)
    return results


# ============== BATCH ==============


@mcp.tool()
def batch_get(urls: list[str]) -> list[dict]:
    """Make up to 10 GET requests in a single API call"""
    if len(urls) > 10:
        raise ValueError("Batch API supports at most 10 URLs")
    endpoint = f"/batch?urls={','.join(quote(u, safe='/?=&') for u in urls)}"
    return trello_request(endpoint)


def main():
    """Entry point for the MCP server"""
    mcp.run()


if __name__ == "__main__":
    main()
