#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

// ============================================
// CONFIGURATION
// ============================================
const MCP_NAME = "clover";
const MCP_VERSION = "1.0.0";

// Clover API base URLs
// Production: https://api.clover.com (US/Canada), https://api.eu.clover.com (Europe), https://api.la.clover.com (LATAM)
// Sandbox: https://apisandbox.dev.clover.com
const API_BASE_URL = process.env.CLOVER_SANDBOX === "true" 
  ? "https://apisandbox.dev.clover.com"
  : (process.env.CLOVER_REGION === "EU" 
    ? "https://api.eu.clover.com" 
    : process.env.CLOVER_REGION === "LA" 
      ? "https://api.la.clover.com"
      : "https://api.clover.com");

// ============================================
// API CLIENT
// ============================================
class CloverClient {
  private apiKey: string;
  private merchantId: string;
  private baseUrl: string;

  constructor(apiKey: string, merchantId: string) {
    this.apiKey = apiKey;
    this.merchantId = merchantId;
    this.baseUrl = API_BASE_URL;
  }

  async request(endpoint: string, options: RequestInit = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    const response = await fetch(url, {
      ...options,
      headers: {
        "Authorization": `Bearer ${this.apiKey}`,
        "Content-Type": "application/json",
        ...options.headers,
      },
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`Clover API error: ${response.status} ${response.statusText} - ${errorText}`);
    }

    return response.json();
  }

  async get(endpoint: string) {
    return this.request(endpoint, { method: "GET" });
  }

  async post(endpoint: string, data: any) {
    return this.request(endpoint, {
      method: "POST",
      body: JSON.stringify(data),
    });
  }

  async put(endpoint: string, data: any) {
    return this.request(endpoint, {
      method: "PUT",
      body: JSON.stringify(data),
    });
  }

  async delete(endpoint: string) {
    return this.request(endpoint, { method: "DELETE" });
  }

  getMerchantId() {
    return this.merchantId;
  }
}

// ============================================
// TOOL DEFINITIONS
// ============================================
const tools = [
  {
    name: "list_orders",
    description: "List orders for the merchant. Returns paginated list of orders with details like totals, state, and timestamps.",
    inputSchema: {
      type: "object" as const,
      properties: {
        limit: { type: "number", description: "Max orders to return (default 100)" },
        offset: { type: "number", description: "Pagination offset" },
        filter: { type: "string", description: "Filter query (e.g., 'state=open')" },
        expand: { type: "string", description: "Expand related objects (e.g., 'lineItems,payments')" },
      },
    },
  },
  {
    name: "get_order",
    description: "Get a specific order by ID with full details including line items, payments, and discounts.",
    inputSchema: {
      type: "object" as const,
      properties: {
        order_id: { type: "string", description: "Order ID" },
        expand: { type: "string", description: "Expand related objects (e.g., 'lineItems,payments,discounts')" },
      },
      required: ["order_id"],
    },
  },
  {
    name: "create_order",
    description: "Create a new order. Use atomic_order for complete orders with line items in one call.",
    inputSchema: {
      type: "object" as const,
      properties: {
        state: { type: "string", description: "Order state: 'open', 'locked', etc." },
        title: { type: "string", description: "Order title/note" },
        note: { type: "string", description: "Additional order notes" },
        order_type_id: { type: "string", description: "Order type ID" },
        line_items: { 
          type: "array", 
          description: "Array of line items with item_id, quantity, and optional modifications",
          items: {
            type: "object",
            properties: {
              item_id: { type: "string" },
              quantity: { type: "number" },
              price: { type: "number" },
              name: { type: "string" },
            }
          }
        },
      },
    },
  },
  {
    name: "list_items",
    description: "List inventory items (products) available for sale. Returns item details, prices, and stock info.",
    inputSchema: {
      type: "object" as const,
      properties: {
        limit: { type: "number", description: "Max items to return (default 100)" },
        offset: { type: "number", description: "Pagination offset" },
        filter: { type: "string", description: "Filter by name, SKU, etc." },
        expand: { type: "string", description: "Expand related objects (e.g., 'categories,modifierGroups,tags')" },
      },
    },
  },
  {
    name: "get_inventory",
    description: "Get inventory stock counts for items. Shows current quantity and tracking status.",
    inputSchema: {
      type: "object" as const,
      properties: {
        item_id: { type: "string", description: "Specific item ID (optional - omit to get all)" },
      },
    },
  },
  {
    name: "list_customers",
    description: "List customers in the merchant's customer database. Includes contact info and marketing preferences.",
    inputSchema: {
      type: "object" as const,
      properties: {
        limit: { type: "number", description: "Max customers to return (default 100)" },
        offset: { type: "number", description: "Pagination offset" },
        filter: { type: "string", description: "Filter by name, email, phone" },
        expand: { type: "string", description: "Expand related objects (e.g., 'addresses,emailAddresses,phoneNumbers')" },
      },
    },
  },
  {
    name: "list_payments",
    description: "List payments processed by the merchant. Includes payment method, amount, status, and related order.",
    inputSchema: {
      type: "object" as const,
      properties: {
        limit: { type: "number", description: "Max payments to return (default 100)" },
        offset: { type: "number", description: "Pagination offset" },
        filter: { type: "string", description: "Filter by result (SUCCESS, DECLINED, etc.)" },
        expand: { type: "string", description: "Expand related objects (e.g., 'tender,order')" },
      },
    },
  },
  {
    name: "get_merchant",
    description: "Get merchant account information including business name, address, timezone, and settings.",
    inputSchema: {
      type: "object" as const,
      properties: {
        expand: { type: "string", description: "Expand related objects (e.g., 'address,openingHours,owner')" },
      },
    },
  },
];

// ============================================
// TOOL HANDLERS
// ============================================
async function handleTool(client: CloverClient, name: string, args: any) {
  const mId = client.getMerchantId();
  
  switch (name) {
    case "list_orders": {
      const { limit = 100, offset = 0, filter, expand } = args;
      let endpoint = `/v3/merchants/${mId}/orders?limit=${limit}&offset=${offset}`;
      if (filter) endpoint += `&filter=${encodeURIComponent(filter)}`;
      if (expand) endpoint += `&expand=${encodeURIComponent(expand)}`;
      return await client.get(endpoint);
    }
    
    case "get_order": {
      const { order_id, expand } = args;
      let endpoint = `/v3/merchants/${mId}/orders/${order_id}`;
      if (expand) endpoint += `?expand=${encodeURIComponent(expand)}`;
      return await client.get(endpoint);
    }
    
    case "create_order": {
      const { state = "open", title, note, order_type_id, line_items } = args;
      
      // If line_items provided, use atomic order endpoint
      if (line_items && line_items.length > 0) {
        const orderData: any = {
          orderCart: {
            lineItems: line_items.map((item: any) => ({
              item: item.item_id ? { id: item.item_id } : undefined,
              name: item.name,
              price: item.price,
              unitQty: item.quantity || 1,
            })),
          },
        };
        if (title) orderData.orderCart.title = title;
        if (note) orderData.orderCart.note = note;
        if (order_type_id) orderData.orderCart.orderType = { id: order_type_id };
        
        return await client.post(`/v3/merchants/${mId}/atomic_order/orders`, orderData);
      }
      
      // Simple order creation
      const orderData: any = { state };
      if (title) orderData.title = title;
      if (note) orderData.note = note;
      if (order_type_id) orderData.orderType = { id: order_type_id };
      
      return await client.post(`/v3/merchants/${mId}/orders`, orderData);
    }
    
    case "list_items": {
      const { limit = 100, offset = 0, filter, expand } = args;
      let endpoint = `/v3/merchants/${mId}/items?limit=${limit}&offset=${offset}`;
      if (filter) endpoint += `&filter=${encodeURIComponent(filter)}`;
      if (expand) endpoint += `&expand=${encodeURIComponent(expand)}`;
      return await client.get(endpoint);
    }
    
    case "get_inventory": {
      const { item_id } = args;
      if (item_id) {
        return await client.get(`/v3/merchants/${mId}/item_stocks/${item_id}`);
      }
      return await client.get(`/v3/merchants/${mId}/item_stocks`);
    }
    
    case "list_customers": {
      const { limit = 100, offset = 0, filter, expand } = args;
      let endpoint = `/v3/merchants/${mId}/customers?limit=${limit}&offset=${offset}`;
      if (filter) endpoint += `&filter=${encodeURIComponent(filter)}`;
      if (expand) endpoint += `&expand=${encodeURIComponent(expand)}`;
      return await client.get(endpoint);
    }
    
    case "list_payments": {
      const { limit = 100, offset = 0, filter, expand } = args;
      let endpoint = `/v3/merchants/${mId}/payments?limit=${limit}&offset=${offset}`;
      if (filter) endpoint += `&filter=${encodeURIComponent(filter)}`;
      if (expand) endpoint += `&expand=${encodeURIComponent(expand)}`;
      return await client.get(endpoint);
    }
    
    case "get_merchant": {
      const { expand } = args;
      let endpoint = `/v3/merchants/${mId}`;
      if (expand) endpoint += `?expand=${encodeURIComponent(expand)}`;
      return await client.get(endpoint);
    }
    
    default:
      throw new Error(`Unknown tool: ${name}`);
  }
}

// ============================================
// SERVER SETUP
// ============================================
async function main() {
  const apiKey = process.env.CLOVER_API_KEY;
  const merchantId = process.env.CLOVER_MERCHANT_ID;
  
  if (!apiKey) {
    console.error("Error: CLOVER_API_KEY environment variable required");
    process.exit(1);
  }
  
  if (!merchantId) {
    console.error("Error: CLOVER_MERCHANT_ID environment variable required");
    process.exit(1);
  }

  const client = new CloverClient(apiKey, merchantId);

  const server = new Server(
    { name: `${MCP_NAME}-mcp`, version: MCP_VERSION },
    { capabilities: { tools: {} } }
  );

  server.setRequestHandler(ListToolsRequestSchema, async () => ({
    tools,
  }));

  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    
    try {
      const result = await handleTool(client, name, args || {});
      return {
        content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
      };
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      return {
        content: [{ type: "text", text: `Error: ${message}` }],
        isError: true,
      };
    }
  });

  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error(`${MCP_NAME} MCP server running on stdio`);
}

main().catch(console.error);
