"""Contact manager — a FastMCPApp example with forms and callable tool references.

Demonstrates the full FastMCPApp stack:
- @app.ui() entry point that the model calls to open the app
- @app.tool() backend tools that the UI calls via CallTool
- CallTool(fn) with function references (not strings) that resolve to global keys
- Form.from_model() for auto-generated Pydantic model forms
- Manual form construction with the context-manager pattern

Usage:
    uv run python contacts_server.py
"""

from __future__ import annotations

from typing import Literal

from prefab_ui.actions import SetState, ShowToast
from prefab_ui.actions.mcp import CallTool
from prefab_ui.app import PrefabApp
from prefab_ui.components import (
    Badge,
    Button,
    Column,
    ForEach,
    Form,
    Heading,
    Input,
    Muted,
    Row,
    Separator,
    Text,
)
from prefab_ui.rx import ERROR, RESULT, STATE
from pydantic import BaseModel, Field

from fastmcp import FastMCP, FastMCPApp

# ---------------------------------------------------------------------------
# Data
# ---------------------------------------------------------------------------

_contacts: list[dict] = [
    {
        "name": "Arthur Dent",
        "email": "arthur@earth.com",
        "category": "Customer",
        "notes": "",
    },
    {
        "name": "Ford Prefect",
        "email": "ford@betelgeuse.org",
        "category": "Partner",
        "notes": "Researcher",
    },
]


# ---------------------------------------------------------------------------
# Pydantic model for auto-generated forms
# ---------------------------------------------------------------------------


class ContactModel(BaseModel):
    name: str = Field(title="Full Name", min_length=1)
    email: str = Field(title="Email")
    category: Literal["Customer", "Vendor", "Partner", "Other"] = "Other"
    notes: str = Field(
        default="",
        title="Notes",
        json_schema_extra={"ui": {"type": "textarea"}},
    )


# ---------------------------------------------------------------------------
# App
# ---------------------------------------------------------------------------

app = FastMCPApp("Contacts")


@app.tool()
def save_contact(data: ContactModel) -> list[dict]:
    """Save a new contact and return the updated list."""
    _contacts.append(data.model_dump())
    return list(_contacts)


@app.tool()
def search_contacts(query: str) -> list[dict]:
    """Filter contacts by name or email."""
    q = query.lower()
    return [c for c in _contacts if q in c["name"].lower() or q in c["email"].lower()]


@app.tool(model=True)
def list_contacts() -> list[dict]:
    """Return all contacts. Visible to both the model and the UI."""
    return list(_contacts)


@app.ui()
def contact_manager() -> PrefabApp:
    """Open the contact manager. The model calls this to launch the app."""
    with Column(gap=6, css_class="p-6") as view:
        Heading("Contacts")

        with ForEach("contacts") as contact:
            with Row(gap=2, align="center"):
                Text(contact.name, css_class="font-medium")
                Muted(contact.email)
                Badge(contact.category)

        Separator()

        Heading("Add Contact", level=3)
        Form.from_model(
            ContactModel,
            on_submit=CallTool(
                save_contact,
                on_success=[
                    SetState("contacts", RESULT),
                    ShowToast("Contact saved!", variant="success"),
                ],
                on_error=ShowToast(ERROR, variant="error"),
            ),
        )

        Separator()

        Heading("Search", level=3)
        with Form(
            on_submit=CallTool(
                search_contacts,
                arguments={"query": STATE.query},
                on_success=SetState("contacts", RESULT),
            )
        ):
            Input(name="query", placeholder="Search by name or email...")
            Button("Search")

    return PrefabApp(view=view, state={"contacts": list(_contacts)})


mcp = FastMCP("Contacts Server", providers=[app])

if __name__ == "__main__":
    mcp.run(transport="http")
