"""Order fulfillment tools for Katana MCP Server.

Foundation tools for fulfilling manufacturing orders and sales orders.

These tools provide:
- fulfill_order: Complete manufacturing orders or fulfill sales orders
"""

from __future__ import annotations

import logging
from datetime import UTC, datetime
from typing import Annotated, Literal

from fastmcp import Context, FastMCP
from fastmcp.tools.tool import ToolResult
from pydantic import BaseModel, Field

from katana_mcp.logging import observe_tool
from katana_mcp.services import get_services
from katana_mcp.tools.schemas import ConfirmationSchema
from katana_mcp.tools.tool_result_utils import make_tool_result
from katana_mcp.unpack import Unpack, unpack_pydantic_params
from katana_public_api_client.domain.converters import unwrap_unset
from katana_public_api_client.models import (
    CreateSalesOrderFulfillmentBody,
    ManufacturingOrder,
    SalesOrder,
    UpdateManufacturingOrderRequest,
)
from katana_public_api_client.utils import is_success, unwrap, unwrap_as

logger = logging.getLogger(__name__)

# ============================================================================
# Tool: fulfill_order
# ============================================================================


class FulfillOrderRequest(BaseModel):
    """Request to fulfill an order."""

    order_id: int = Field(..., description="Order ID to fulfill")
    order_type: Literal["manufacturing", "sales"] = Field(
        ..., description="Type of order (manufacturing or sales)"
    )
    confirm: bool = Field(
        False, description="If false, returns preview. If true, fulfills order."
    )


class FulfillOrderResponse(BaseModel):
    """Response from fulfilling an order."""

    order_id: int
    order_type: str
    order_number: str
    status: str
    is_preview: bool
    inventory_updates: list[str] = Field(
        default_factory=list, description="Inventory changes made or to be made"
    )
    warnings: list[str] = Field(default_factory=list, description="Warning messages")
    next_actions: list[str] = Field(
        default_factory=list, description="Suggested next steps"
    )
    message: str


def _fulfill_response_to_tool_result(response: FulfillOrderResponse) -> ToolResult:
    """Convert FulfillOrderResponse to ToolResult with markdown template."""
    # Format lists for template
    inventory_updates_text = (
        "\n".join(f"- {update}" for update in response.inventory_updates)
        if response.inventory_updates
        else "No inventory updates"
    )

    next_steps_text = (
        "\n".join(f"- {action}" for action in response.next_actions)
        if response.next_actions
        else "No next steps"
    )

    return make_tool_result(
        response,
        "order_fulfilled",
        order_type=response.order_type.title(),
        order_number=response.order_number,
        order_id=response.order_id,
        fulfilled_at=datetime.now(UTC).isoformat(),
        items_count="N/A",  # Not available in fulfill response
        total_value="N/A",  # Not available in fulfill response
        status=response.status,
        inventory_updates=inventory_updates_text,
        next_steps=next_steps_text,
    )


async def _fulfill_order_impl(
    request: FulfillOrderRequest, context: Context
) -> FulfillOrderResponse:
    """Implementation of fulfill_order tool.

    Fulfills manufacturing orders by marking them as DONE, or fulfills sales
    orders by creating a sales order fulfillment.

    Manufacturing orders:
    - Fetches the order to get current status
    - Preview mode: Shows what would happen (status change to DONE)
    - Confirm mode: Updates order status to DONE via PATCH /manufacturing_orders/{id}

    Sales orders:
    - Fetches the order to get current status
    - Preview mode: Shows what would happen (create fulfillment)
    - Confirm mode: Creates fulfillment via POST /sales_order_fulfillments

    Args:
        request: Request with order ID, type, and confirm flag
        context: Server context with KatanaClient

    Returns:
        FulfillOrderResponse with details of fulfillment

    Raises:
        ValueError: If validation fails
        Exception: If API call fails
    """
    logger.info(
        f"{'Previewing' if not request.confirm else 'Fulfilling'} {request.order_type} order {request.order_id}"
    )

    services = get_services(context)

    try:
        if request.order_type == "manufacturing":
            # Fetch manufacturing order
            from katana_public_api_client.api.manufacturing_order import (
                get_manufacturing_order as api_get_manufacturing_order,
            )

            mo_response = await api_get_manufacturing_order.asyncio_detailed(
                id=request.order_id, client=services.client
            )

            # unwrap_as() raises typed exceptions on error, returns typed ManufacturingOrder
            mo = unwrap_as(mo_response, ManufacturingOrder)

            # Extract order number safely using unwrap_unset
            order_number = unwrap_unset(mo.order_no, f"MO-{request.order_id}")

            # Extract current status
            current_status = mo.status.value if mo.status else "UNKNOWN"

            # Build inventory update messages
            inventory_updates = [
                "Manufacturing order completion will update inventory based on BOM",
                "Finished goods will be added to stock",
                "Raw materials will be consumed from inventory",
            ]

            # Check current status and provide warnings
            warnings = []
            if current_status == "DONE":
                warnings.append(
                    f"Manufacturing order {order_number} is already completed"
                )
            elif current_status == "BLOCKED":
                warnings.append(
                    f"Manufacturing order {order_number} is blocked - review before completing"
                )

            # Preview mode - return what would happen
            if not request.confirm:
                logger.info(
                    f"Preview mode: Would complete manufacturing order {order_number}"
                )

                next_actions = [
                    "Review the manufacturing order details",
                    "Verify all production steps are complete",
                    "Set confirm=true to mark order as DONE",
                ]

                if current_status == "DONE":
                    next_actions = ["Order is already completed - no action needed"]

                return FulfillOrderResponse(
                    order_id=request.order_id,
                    order_type="manufacturing",
                    order_number=order_number,
                    status=current_status,
                    is_preview=True,
                    inventory_updates=inventory_updates,
                    warnings=warnings,
                    next_actions=next_actions,
                    message=f"Preview: Would mark manufacturing order {order_number} as DONE (currently {current_status})",
                )

            # Confirm mode - use elicitation to get user confirmation
            if current_status == "DONE":
                logger.info(f"Manufacturing order {order_number} is already completed")
                return FulfillOrderResponse(
                    order_id=request.order_id,
                    order_type="manufacturing",
                    order_number=order_number,
                    status=current_status,
                    is_preview=False,
                    inventory_updates=[],
                    warnings=warnings,
                    next_actions=["Order is already completed"],
                    message=f"Manufacturing order {order_number} is already completed",
                )

            # Get user confirmation before marking as done
            elicit_result = await context.elicit(
                f"Mark manufacturing order {order_number} as DONE and update inventory?",
                ConfirmationSchema,
            )

            # Check if user accepted
            if elicit_result.action != "accept":
                logger.info(
                    f"User did not accept fulfillment of manufacturing order {order_number}"
                )
                return FulfillOrderResponse(
                    order_id=request.order_id,
                    order_type="manufacturing",
                    order_number=order_number,
                    status=current_status,
                    is_preview=True,
                    inventory_updates=inventory_updates,
                    warnings=warnings,
                    message="Manufacturing order fulfillment cancelled by user",
                    next_actions=[
                        "Review the order details and try again with confirm=true"
                    ],
                )

            # Type narrowing: at this point we know action == "accept", so data exists
            if not elicit_result.data.confirm:
                logger.info(
                    f"User declined to confirm fulfillment of manufacturing order {order_number}"
                )
                return FulfillOrderResponse(
                    order_id=request.order_id,
                    order_type="manufacturing",
                    order_number=order_number,
                    status=current_status,
                    is_preview=True,
                    inventory_updates=inventory_updates,
                    warnings=warnings,
                    message="Manufacturing order fulfillment declined by user",
                    next_actions=[
                        "Review the order details and try again with confirm=true"
                    ],
                )

            # User confirmed - update order status to DONE
            from katana_public_api_client.api.manufacturing_order import (
                update_manufacturing_order as api_update_manufacturing_order,
            )

            update_request = UpdateManufacturingOrderRequest()
            # Set status via additional_properties since status field is not in the model
            update_request.additional_properties["status"] = "DONE"

            update_response = await api_update_manufacturing_order.asyncio_detailed(
                id=request.order_id, client=services.client, body=update_request
            )

            # unwrap_as() raises typed exceptions on error, returns typed ManufacturingOrder
            updated_mo = unwrap_as(update_response, ManufacturingOrder)
            new_status = updated_mo.status.value if updated_mo.status else "UNKNOWN"

            logger.info(
                f"Successfully marked manufacturing order {order_number} as DONE"
            )

            return FulfillOrderResponse(
                order_id=request.order_id,
                order_type="manufacturing",
                order_number=order_number,
                status=new_status,
                is_preview=False,
                inventory_updates=inventory_updates,
                warnings=warnings,
                next_actions=[
                    f"Manufacturing order {order_number} completed",
                    "Inventory has been updated",
                    "Check stock levels for finished goods",
                ],
                message=f"Successfully marked manufacturing order {order_number} as DONE",
            )

        else:  # sales order
            # Fetch sales order
            from katana_public_api_client.api.sales_order import (
                get_sales_order as api_get_sales_order,
            )

            so_response = await api_get_sales_order.asyncio_detailed(
                id=request.order_id, client=services.client
            )

            # unwrap_as() raises typed exceptions on error, returns typed SalesOrder
            so = unwrap_as(so_response, SalesOrder)

            # Extract order number safely using unwrap_unset
            order_number = unwrap_unset(so.order_no, f"SO-{request.order_id}")

            # Extract current status
            current_status = so.status.value if so.status else "UNKNOWN"

            # Build inventory update messages
            inventory_updates = [
                "Sales order fulfillment will reduce available inventory",
                "Items will be marked as shipped/fulfilled",
                "Stock levels will be updated accordingly",
            ]

            # Check current status and provide warnings
            warnings = []
            if current_status in ["DELIVERED", "PARTIALLY_DELIVERED"]:
                warnings.append(f"Sales order {order_number} may already be delivered")

            # Preview mode - return what would happen
            if not request.confirm:
                logger.info(f"Preview mode: Would fulfill sales order {order_number}")

                next_actions = [
                    "Review the sales order details",
                    "Verify items are ready to ship",
                    "Set confirm=true to create fulfillment",
                ]

                if current_status in ["DELIVERED", "PARTIALLY_DELIVERED"]:
                    next_actions = [
                        "Order may already be delivered - verify before creating additional fulfillment"
                    ]

                return FulfillOrderResponse(
                    order_id=request.order_id,
                    order_type="sales",
                    order_number=order_number,
                    status=current_status,
                    is_preview=True,
                    inventory_updates=inventory_updates,
                    warnings=warnings,
                    next_actions=next_actions,
                    message=f"Preview: Would fulfill sales order {order_number} (currently {current_status})",
                )

            # Confirm mode - use elicitation to get user confirmation before creating fulfillment
            # Note: Sales orders in Katana can have multiple fulfillments, so we don't
            # prevent fulfillment based on status

            # Get user confirmation
            elicit_result = await context.elicit(
                f"Create fulfillment for sales order {order_number} and update inventory?",
                ConfirmationSchema,
            )

            # Check if user accepted
            if elicit_result.action != "accept":
                logger.info(
                    f"User did not accept fulfillment of sales order {order_number}"
                )
                return FulfillOrderResponse(
                    order_id=request.order_id,
                    order_type="sales",
                    order_number=order_number,
                    status=current_status,
                    is_preview=True,
                    inventory_updates=inventory_updates,
                    warnings=warnings,
                    message="Sales order fulfillment cancelled by user",
                    next_actions=[
                        "Review the order details and try again with confirm=true"
                    ],
                )

            # Type narrowing: at this point we know action == "accept", so data exists
            if not elicit_result.data.confirm:
                logger.info(
                    f"User declined to confirm fulfillment of sales order {order_number}"
                )
                return FulfillOrderResponse(
                    order_id=request.order_id,
                    order_type="sales",
                    order_number=order_number,
                    status=current_status,
                    is_preview=True,
                    inventory_updates=inventory_updates,
                    warnings=warnings,
                    message="Sales order fulfillment declined by user",
                    next_actions=[
                        "Review the order details and try again with confirm=true"
                    ],
                )

            # User confirmed - create sales order fulfillment
            from katana_public_api_client.api.sales_order_fulfillment import (
                create_sales_order_fulfillment as api_create_sales_order_fulfillment,
            )

            fulfillment_body = CreateSalesOrderFulfillmentBody(
                sales_order_id=request.order_id
            )

            fulfillment_response = (
                await api_create_sales_order_fulfillment.asyncio_detailed(
                    client=services.client, body=fulfillment_body
                )
            )

            # Use is_success for 201 Created response
            if not is_success(fulfillment_response):
                # unwrap will raise with appropriate error details
                unwrap(fulfillment_response)

            logger.info(
                f"Successfully created fulfillment for sales order {order_number}"
            )

            return FulfillOrderResponse(
                order_id=request.order_id,
                order_type="sales",
                order_number=order_number,
                status="FULFILLED",
                is_preview=False,
                inventory_updates=inventory_updates,
                warnings=warnings,
                next_actions=[
                    f"Sales order {order_number} fulfilled",
                    "Inventory has been updated",
                    "Fulfillment record created",
                ],
                message=f"Successfully fulfilled sales order {order_number}",
            )

    except Exception as e:
        logger.error(f"Failed to fulfill {request.order_type} order: {e}")
        raise


@observe_tool
@unpack_pydantic_params
async def fulfill_order(
    request: Annotated[FulfillOrderRequest, Unpack()], context: Context
) -> ToolResult:
    """Fulfill a manufacturing order or sales order with two-step confirmation.

    This tool supports a two-step confirmation process:
    1. Preview (confirm=false): Shows what would happen without making changes
    2. Confirm (confirm=true): Actually fulfills the order

    Manufacturing Orders:
    - Marks the order as DONE via PATCH /manufacturing_orders/{id}
    - Updates inventory based on bill of materials (BOM)
    - Adds finished goods to stock
    - Consumes raw materials from inventory

    Sales Orders:
    - Creates a fulfillment via POST /sales_order_fulfillments
    - Reduces available inventory
    - Marks items as shipped/fulfilled
    - Creates fulfillment record

    The tool handles different order statuses appropriately and provides
    warnings for edge cases (already completed, cancelled, blocked, etc.).

    Args:
        request: Request with order ID, type, and confirm flag
        context: Server context with KatanaClient

    Returns:
        ToolResult with markdown content and structured data

    Example:
        Preview Manufacturing Order:
            Request: {"order_id": 1234, "order_type": "manufacturing", "confirm": false}
            Returns: Preview showing order would be marked as DONE

        Confirm Manufacturing Order:
            Request: {"order_id": 1234, "order_type": "manufacturing", "confirm": true}
            Returns: Success message with updated status

        Preview Sales Order:
            Request: {"order_id": 5678, "order_type": "sales", "confirm": false}
            Returns: Preview showing fulfillment would be created

        Confirm Sales Order:
            Request: {"order_id": 5678, "order_type": "sales", "confirm": true}
            Returns: Success message with fulfillment details
    """
    response = await _fulfill_order_impl(request, context)
    return _fulfill_response_to_tool_result(response)


def register_tools(mcp: FastMCP) -> None:
    """Register all order fulfillment tools with the FastMCP instance.

    Registers the fulfill_order tool for completing manufacturing orders
    and fulfilling sales orders.

    Args:
        mcp: FastMCP server instance to register tools with
    """
    mcp.tool()(fulfill_order)
