"""FastMCP server — thin tool wrappers over controller methods.

Tools are minimal: validate args, call controller, return result.
All business logic lives in controllers.py.

Usage:
    # stdio (for Claude Code)
    platoon mcp

    # or directly
    python -m platoon.mcp.server
"""

from __future__ import annotations

from typing import Any, Optional

from fastmcp import FastMCP

from platoon.mcp import controllers

MCP_INSTRUCTIONS = """Platoon is an AI training suite for commerce analytics.

Available tools:
- **read_file**: Load CSV or text files. Use this first to inspect data.
- **forecast**: Run demand forecasting on time series CSV data.
- **optimize**: Run inventory optimization (EOQ, safety stock, reorder points, ABC).
- **detect_anomalies**: Find outliers/spikes/drops in demand time series.
- **basket_analysis**: Discover "frequently bought together" patterns from orders.
- **cashflow**: Project daily revenue, COGS, and reorder costs over a horizon.
- **schedule**: Assign staff to shifts based on demand and availability.

Typical workflow:
1. read_file to inspect the CSV columns and sample rows
2. forecast or optimize with the file path and relevant parameters
3. detect_anomalies to flag unusual demand patterns
4. basket_analysis to find cross-sell opportunities
5. cashflow for financial projections
6. schedule for staff planning
"""


def create_mcp_server() -> FastMCP:
    """Create the Platoon MCP server with learning tools."""
    mcp = FastMCP(name="platoon", instructions=MCP_INSTRUCTIONS)

    @mcp.tool()
    async def read_file(path: str, head: int = 0) -> dict[str, Any]:
        """Read a CSV or text file. Returns rows (for CSV) or text content.

        Args:
            path: File path to read.
            head: If > 0, return only the first N rows/lines.
        """
        return controllers.read_file(path, head=head)

    @mcp.tool()
    async def forecast(
        data_path: str,
        product_id: Optional[str] = None,
        horizon: int = 14,
        method: str = "auto",
        season_length: int = 7,
        holdout: int = 30,
    ) -> dict[str, Any]:
        """Run demand forecasting on a CSV time series.

        CSV must have columns: date, product_id, units_sold.
        Returns forecast values, confidence intervals, trend, seasonality, and accuracy (MAE).

        Args:
            data_path: Path to daily demand CSV file.
            product_id: Which product to forecast. Picks highest-volume if omitted.
            horizon: Days ahead to forecast (default 14). Must be >= 1.
            method: auto, moving_average, exponential_smoothing, croston, arima, ets, theta.
            season_length: Seasonal period in days (default 7).
            holdout: Days to hold out for accuracy eval (default 30).
        """
        return controllers.forecast(
            data_path, product_id=product_id, horizon=horizon,
            method=method, season_length=season_length, holdout=holdout,
        )

    @mcp.tool()
    async def optimize(
        data_path: str,
        product_id: Optional[str] = None,
        orders_path: Optional[str] = None,
        inventory_path: Optional[str] = None,
        service_level: float = 0.95,
    ) -> dict[str, Any]:
        """Run inventory optimization on product data.

        Computes EOQ, safety stock, reorder point, ABC class, stockout risk,
        plus price, cost, daily_revenue, restock_cost, and days_of_stock
        so the agent can compute ROI and revenue-at-risk without extra calls.

        Args:
            data_path: Path to products CSV (product_id, sku, cost, price, base_daily_demand, lead_time_days).
            product_id: Analyze a single product. If omitted, analyzes all.
            orders_path: Optional orders CSV for ABC classification.
            inventory_path: Optional inventory CSV for current stock levels.
            service_level: Target service level (0-1 exclusive, default 0.95).
        """
        return controllers.optimize(
            data_path, product_id=product_id, orders_path=orders_path,
            inventory_path=inventory_path, service_level=service_level,
        )

    @mcp.tool()
    async def detect_anomalies(
        data_path: str,
        product_id: Optional[str] = None,
        method: str = "zscore",
        window: int = 30,
        threshold: float = 2.5,
    ) -> dict[str, Any]:
        """Detect anomalies (spikes/drops) in a demand time series.

        Uses rolling-window statistical methods to flag outlier data points.
        Returns anomaly details with severity, direction, and z-scores.

        Args:
            data_path: Path to daily demand CSV (date, product_id, units_sold).
            product_id: Which product to analyze. Picks highest-volume if omitted.
            method: Detection method — "zscore" or "iqr".
            window: Rolling window size in days (default 30).
            threshold: Z-score threshold for flagging (default 2.5, zscore only).
        """
        return controllers.detect_anomalies(
            data_path, product_id=product_id, method=method,
            window=window, threshold=threshold,
        )

    @mcp.tool()
    async def basket_analysis(
        orders_path: str,
        min_support: float = 0.01,
        min_confidence: float = 0.3,
        max_rules: int = 50,
    ) -> dict[str, Any]:
        """Find association rules ("frequently bought together") from order data.

        Groups orders by order_id into transactions, computes co-occurrence rules
        with support, confidence, and lift metrics.

        Args:
            orders_path: Path to orders CSV (order_id, product_id).
            min_support: Minimum support threshold (default 0.01).
            min_confidence: Minimum confidence threshold (default 0.3).
            max_rules: Maximum rules to return (default 50).
        """
        return controllers.basket_analysis(
            orders_path, min_support=min_support,
            min_confidence=min_confidence, max_rules=max_rules,
        )

    @mcp.tool()
    async def cashflow(
        data_path: str,
        demand_path: str,
        inventory_path: Optional[str] = None,
        horizon: int = 30,
        product_id: Optional[str] = None,
    ) -> dict[str, Any]:
        """Project daily cash flow (revenue, COGS, reorder costs) over a horizon.

        Composes product prices + demand history + inventory state into daily
        financial projections with reorder event simulation.

        Args:
            data_path: Path to products CSV.
            demand_path: Path to daily demand CSV.
            inventory_path: Optional inventory CSV for stock tracking and reorder simulation.
            horizon: Days to project forward (default 30).
            product_id: Analyze a single product. If omitted, analyzes all.
        """
        return controllers.cashflow(
            data_path, demand_path, inventory_path=inventory_path,
            horizon=horizon, product_id=product_id,
        )

    @mcp.tool()
    async def schedule(
        demand_path: str,
        staff_path: str,
        shift_hours: float = 8.0,
        min_coverage: float = 1.0,
        horizon_days: int = 7,
        product_id: Optional[str] = None,
        slots_path: Optional[str] = None,
    ) -> dict[str, Any]:
        """Schedule staff shifts based on demand signal and availability.

        Aggregates demand into day-of-week slots and assigns staff using
        cost-minimizing greedy algorithm.

        When slots_path is provided, uses explicit slot definitions with optional
        skill requirements instead of demand aggregation. This enables skill-based
        scheduling (e.g. tutor scheduling where math slots need math-skilled staff).

        Args:
            demand_path: Path to daily demand CSV.
            staff_path: Path to staff CSV (staff_id, name, hourly_rate, max_hours_per_week, available_days).
            shift_hours: Hours per shift (default 8).
            min_coverage: Minimum coverage fraction per slot (default 1.0).
            horizon_days: Days to schedule (default 7).
            product_id: If specified, only consider demand from this product.
            slots_path: Optional path to slots CSV (slot_label, hours_needed, required_skills).
                When provided, uses explicit slots with skill matching instead of demand aggregation.
        """
        return controllers.schedule(
            demand_path, staff_path, shift_hours=shift_hours,
            min_coverage=min_coverage, horizon_days=horizon_days,
            product_id=product_id, slots_path=slots_path,
        )

    return mcp


if __name__ == "__main__":
    import asyncio
    mcp = create_mcp_server()
    asyncio.run(mcp.run_async(transport="stdio"))
