"""Dashboard management: list, create, and populate dashboards."""

from __future__ import annotations

from typing import Any

from metaverse.app import mcp
from metaverse.client import as_dict, as_list
from metaverse.client import get as api_get
from metaverse.client import post as api_post
from metaverse.client import put as api_put
from metaverse.config import settings


@mcp.tool()
async def list_dashboards() -> list[dict[str, Any]]:
    """List all dashboards in Metabase.

    Returns [{id, name, description}].
    """
    data = await api_get("/dashboard")
    return [
        {
            "id": d["id"],
            "name": d["name"],
            "description": d.get("description") or "",
        }
        for d in as_list(data)
    ]


@mcp.tool()
async def get_dashboard(dashboard_id: int) -> dict[str, Any]:
    """Get a dashboard with all its cards and layout.

    Args:
        dashboard_id: The dashboard ID from list_dashboards.

    Returns {id, name, description, url, cards: [{dashcard_id, card_id, name, row, col, size_x, size_y}]}.
    """
    data = as_dict(await api_get(f"/dashboard/{dashboard_id}"))
    cards = [
        {
            "dashcard_id": dc["id"],
            "card_id": dc.get("card_id"),
            "name": (dc.get("card") or {}).get("name", ""),
            "row": dc.get("row", 0),
            "col": dc.get("col", 0),
            "size_x": dc.get("size_x", 12),
            "size_y": dc.get("size_y", 8),
        }
        for dc in data.get("dashcards", [])
    ]
    return {
        "id": data["id"],
        "name": data["name"],
        "description": data.get("description") or "",
        "url": f"{settings.url}/dashboard/{data['id']}",
        "cards": cards,
    }


@mcp.tool()
async def create_dashboard(name: str, description: str = "") -> dict[str, Any]:
    """Create a new empty dashboard.

    Args:
        name: Dashboard name.
        description: Optional description.

    Returns {id, name, url}.
    """
    payload: dict[str, Any] = {"name": name, "description": description or None}
    data = as_dict(await api_post("/dashboard", json=payload))
    return {
        "id": data["id"],
        "name": data["name"],
        "url": f"{settings.url}/dashboard/{data['id']}",
    }


def _dashcard_to_payload(dc: dict[str, Any]) -> dict[str, Any]:
    """Serialise a dashcard dict into the shape expected by PUT /dashboard/:id/cards."""
    return {
        "id": dc["id"],
        "card_id": dc.get("card_id"),
        "row": dc["row"],
        "col": dc["col"],
        "size_x": dc["size_x"],
        "size_y": dc["size_y"],
        "series": dc.get("series", []),
        "parameter_mappings": dc.get("parameter_mappings", []),
        "visualization_settings": dc.get("visualization_settings", {}),
    }


def _put_cards_result(raw: Any, dashboard_id: int) -> dict[str, Any]:
    """Normalise the PUT /dashboard/:id/cards response into a standard return value."""
    cards = raw if isinstance(raw, list) else (raw or {}).get("cards", [])
    return {
        "dashboard_id": dashboard_id,
        "card_count": len(cards),
        "url": f"{settings.url}/dashboard/{dashboard_id}",
    }


@mcp.tool()
async def add_card_to_dashboard(
    dashboard_id: int,
    card_id: int,
    row: int = 0,
    col: int = 0,
    size_x: int = 12,
    size_y: int = 8,
) -> dict[str, Any]:
    """Add a saved question (card) to a dashboard.

    Args:
        dashboard_id: The target dashboard ID.
        card_id: The question ID to add (from create_question or list_questions).
        row: Grid row position (0-indexed). Increment by size_y for each new row.
        col: Grid column position (0-indexed, max 24).
        size_x: Card width in grid units (1–24, default 12 = half width).
        size_y: Card height in grid units (default 8).

    Returns {dashboard_id, card_count, url}.
    """
    current = as_dict(await api_get(f"/dashboard/{dashboard_id}"))
    existing: list[dict[str, Any]] = current.get("dashcards", [])

    new_card: dict[str, Any] = {
        "id": -1,
        "card_id": card_id,
        "row": row,
        "col": col,
        "size_x": size_x,
        "size_y": size_y,
        "series": [],
        "parameter_mappings": [],
        "visualization_settings": {},
    }
    payload: dict[str, Any] = {"cards": [_dashcard_to_payload(dc) for dc in existing] + [new_card]}
    result = await api_put(f"/dashboard/{dashboard_id}/cards", json=payload)
    return _put_cards_result(result, dashboard_id)


@mcp.tool()
async def remove_card_from_dashboard(dashboard_id: int, card_id: int) -> dict[str, Any]:
    """Remove a saved question (card) from a dashboard.

    Args:
        dashboard_id: The dashboard ID.
        card_id: The question ID to remove (from create_question or list_questions).

    Returns {dashboard_id, card_count, url}.
    """
    current = as_dict(await api_get(f"/dashboard/{dashboard_id}"))
    remaining = [dc for dc in current.get("dashcards", []) if dc.get("card_id") != card_id]
    payload: dict[str, Any] = {"cards": [_dashcard_to_payload(dc) for dc in remaining]}
    result = await api_put(f"/dashboard/{dashboard_id}/cards", json=payload)
    return _put_cards_result(result, dashboard_id)


@mcp.tool()
async def update_card_on_dashboard(
    dashboard_id: int,
    card_id: int,
    row: int | None = None,
    col: int | None = None,
    size_x: int | None = None,
    size_y: int | None = None,
) -> dict[str, Any]:
    """Reposition or resize a card already on a dashboard.

    Only the fields you provide will change; everything else is preserved.

    Args:
        dashboard_id: The dashboard ID.
        card_id: The question ID of the card to update.
        row: New row position, if changing.
        col: New column position, if changing.
        size_x: New width in grid units (1–24), if changing.
        size_y: New height in grid units, if changing.

    Returns {dashboard_id, card_count, url}.
    """
    current = as_dict(await api_get(f"/dashboard/{dashboard_id}"))
    updated: list[dict[str, Any]] = []
    for dc in current.get("dashcards", []):
        if dc.get("card_id") == card_id:
            dc = {
                **dc,
                **({"row": row} if row is not None else {}),
                **({"col": col} if col is not None else {}),
                **({"size_x": size_x} if size_x is not None else {}),
                **({"size_y": size_y} if size_y is not None else {}),
            }
        updated.append(_dashcard_to_payload(dc))
    payload: dict[str, Any] = {"cards": updated}
    result = await api_put(f"/dashboard/{dashboard_id}/cards", json=payload)
    return _put_cards_result(result, dashboard_id)
