"""MCP tool server for D365 Commerce Scale Unit APIs."""

from __future__ import annotations

import json
import os
from typing import Any

from fastmcp import FastMCP

from d365_csu.auth import AzureADAuth
from d365_csu.client import CSUClient, CSUError
from d365_csu.endpoints import get_sample_payload, list_endpoints, list_entities
from d365_csu.entities import carts, categories, customers, orders, org_units, products

mcp = FastMCP("D365 Commerce Scale Unit")


# ── Helpers ──────────────────────────────────────────────────────────


def _get_client() -> CSUClient:
    """Build a CSUClient from environment variables."""
    base_url = os.environ.get("CSU_BASE_URL", "")
    oun = os.environ.get("CSU_OUN", "")
    tenant_id = os.environ.get("AZURE_TENANT_ID", "")
    client_id = os.environ.get("AZURE_CLIENT_ID", "")
    client_secret = os.environ.get("AZURE_CLIENT_SECRET", "")
    resource_id = os.environ.get("AZURE_RESOURCE_ID", "")

    if not all([base_url, oun, tenant_id, client_id, client_secret, resource_id]):
        missing = [
            name
            for name, val in {
                "CSU_BASE_URL": base_url,
                "CSU_OUN": oun,
                "AZURE_TENANT_ID": tenant_id,
                "AZURE_CLIENT_ID": client_id,
                "AZURE_CLIENT_SECRET": client_secret,
                "AZURE_RESOURCE_ID": resource_id,
            }.items()
            if not val
        ]
        raise RuntimeError(
            f"Missing required environment variables: {', '.join(missing)}. "
            "Set them before using CSU API tools."
        )

    auth = AzureADAuth(tenant_id, client_id, client_secret, resource_id)
    return CSUClient(base_url, oun, auth)


def _json_result(data: Any) -> str:
    """Serialize a result to a JSON string for MCP responses."""
    return json.dumps(data, indent=2, default=str)


# ── MCP Tools ────────────────────────────────────────────────────────


@mcp.tool()
def csu_list_endpoints(entity: str | None = None) -> str:
    """List available D365 CSU API endpoints with sample payloads.

    Args:
        entity: Filter by entity name (Customers, Products, Carts,
                SalesOrders, Categories, OrgUnits). None returns all.
    """
    eps = list_endpoints(entity)
    return _json_result(eps)


@mcp.tool()
def csu_list_entities() -> str:
    """List all supported entity names."""
    return _json_result(list_entities())


@mcp.tool()
def csu_get_sample_payload(entity: str, operation: str) -> str:
    """Get a sample JSON request body for a specific endpoint.

    Args:
        entity: Entity name (e.g. "Customers").
        operation: Operation name (e.g. "Search", "CreateEntity").
    """
    try:
        payload = get_sample_payload(entity, operation)
        return _json_result(payload)
    except KeyError as exc:
        return str(exc)


@mcp.tool()
def csu_search_customers(keyword: str, top: int = 10, skip: int = 0) -> str:
    """Search D365 customers by keyword.

    Args:
        keyword: Search term (name, email, account number, etc.).
        top: Max results to return.
        skip: Number of results to skip (for pagination).
    """
    client = _get_client()
    result = customers.search_customers(client, keyword, top=top, skip=skip)
    return _json_result(result)


@mcp.tool()
def csu_create_customer(
    first_name: str,
    last_name: str,
    email: str = "",
    phone: str = "",
    street: str = "",
    city: str = "",
    state: str = "",
    zip_code: str = "",
    country: str = "USA",
) -> str:
    """Create a new D365 customer.

    Args:
        first_name: Customer first name.
        last_name: Customer last name.
        email: Email address.
        phone: Phone number.
        street: Street address.
        city: City.
        state: State/province.
        zip_code: Postal code.
        country: Three-letter ISO country code (default USA).
    """
    payload: dict[str, Any] = {
        "AccountNumber": "",
        "CustomerTypeValue": 1,
        "FirstName": first_name,
        "LastName": last_name,
        "Email": email,
        "Phone": phone,
    }
    if street or city or state or zip_code:
        payload["Addresses"] = [
            {
                "Street": street,
                "City": city,
                "State": state,
                "ZipCode": zip_code,
                "ThreeLetterISORegionName": country,
                "AddressTypeValue": 6,
            }
        ]
    client = _get_client()
    result = customers.create_customer(client, payload)
    return _json_result(result)


@mcp.tool()
def csu_search_products(keyword: str, top: int = 10, skip: int = 0) -> str:
    """Search D365 product catalog by keyword.

    Args:
        keyword: Search term for products.
        top: Max results to return.
        skip: Number of results to skip.
    """
    client = _get_client()
    result = products.search_products(client, keyword, top=top, skip=skip)
    return _json_result(result)


@mcp.tool()
def csu_get_product(product_id: int) -> str:
    """Get a D365 product by its record ID.

    Args:
        product_id: The product record ID.
    """
    client = _get_client()
    result = products.get_product(client, product_id)
    return _json_result(result)


@mcp.tool()
def csu_manage_cart(
    action: str,
    cart_id: str = "",
    product_id: int = 0,
    quantity: int = 1,
    line_ids: str = "",
    receipt_email: str = "",
) -> str:
    """Manage a D365 shopping cart.

    Args:
        action: One of: create, get, add_line, remove_lines, checkout.
        cart_id: Cart ID (required for all actions except create).
        product_id: Product record ID (required for add_line).
        quantity: Quantity to add (default 1, used with add_line).
        line_ids: Comma-separated line IDs to remove (for remove_lines).
        receipt_email: Email for checkout receipt.
    """
    client = _get_client()

    if action == "create":
        result = carts.create_empty_cart(client)
    elif action == "get":
        result = carts.get_cart(client, cart_id)
    elif action == "add_line":
        lines = [{"ProductId": product_id, "Quantity": quantity, "UnitOfMeasureSymbol": "ea"}]
        result = carts.add_cart_lines(client, cart_id, lines)
    elif action == "remove_lines":
        ids = [lid.strip() for lid in line_ids.split(",") if lid.strip()]
        result = carts.remove_cart_lines(client, cart_id, ids)
    elif action == "checkout":
        result = carts.checkout(client, cart_id, receipt_email=receipt_email)
    else:
        return f"Unknown cart action: {action}. Use: create, get, add_line, remove_lines, checkout."

    return _json_result(result)


@mcp.tool()
def csu_search_orders(
    customer_account: str = "",
    top: int = 10,
    skip: int = 0,
) -> str:
    """Search D365 sales orders.

    Args:
        customer_account: Filter by customer account number (optional).
        top: Max results.
        skip: Results to skip.
    """
    client = _get_client()
    result = orders.search_orders(
        client,
        customer_account=customer_account or None,
        top=top,
        skip=skip,
    )
    return _json_result(result)


@mcp.tool()
def csu_get_stores(top: int = 50, skip: int = 0) -> str:
    """Get D365 org units / stores.

    Args:
        top: Max results.
        skip: Results to skip.
    """
    client = _get_client()
    result = org_units.search_org_units(client, top=top, skip=skip)
    return _json_result(result)


@mcp.tool()
def csu_get_categories(top: int = 100, skip: int = 0) -> str:
    """Get the D365 channel navigation category hierarchy.

    Args:
        top: Max results.
        skip: Results to skip.
    """
    client = _get_client()
    result = categories.get_channel_navigation_hierarchy(client, top=top, skip=skip)
    return _json_result(result)


@mcp.tool()
def csu_raw_request(
    method: str,
    path: str,
    body: str = "",
) -> str:
    """Make a raw HTTP request to the D365 CSU API.

    Args:
        method: HTTP method (GET, POST, PATCH, DELETE).
        path: API path (e.g. "Customers/Search").
        body: JSON request body as a string (for POST/PATCH).
    """
    client = _get_client()
    parsed_body = json.loads(body) if body else None
    method_upper = method.upper()

    if method_upper == "GET":
        result = client.get(path)
    elif method_upper == "POST":
        result = client.post(path, parsed_body)
    elif method_upper == "PATCH":
        result = client.patch(path, parsed_body)
    elif method_upper == "DELETE":
        result = client.delete(path)
    else:
        return f"Unsupported HTTP method: {method}"

    return _json_result(result)


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


if __name__ == "__main__":
    main()
