import { z } from 'zod';
import { StripeClient } from '../clients/stripe.js';
import { Invoice, InvoiceItem } from '../types/index.js';

const metadataSchema = z.record(z.string()).optional().describe('Set of key-value pairs for storing additional information');

export default [
  {
    name: 'stripe_list_invoices',
    description: 'List all invoices with optional filtering and pagination',
    inputSchema: z.object({
      limit: z.number().min(1).max(100).optional().describe('Number of invoices to return (1-100)'),
      starting_after: z.string().optional().describe('Cursor for pagination'),
      ending_before: z.string().optional().describe('Cursor for pagination'),
      customer: z.string().optional().describe('Filter by customer ID'),
      subscription: z.string().optional().describe('Filter by subscription ID'),
      status: z.enum(['draft', 'open', 'paid', 'uncollectible', 'void']).optional().describe('Filter by status'),
      collection_method: z.enum(['charge_automatically', 'send_invoice']).optional(),
      created: z.union([
        z.number(),
        z.object({
          gt: z.number().optional(),
          gte: z.number().optional(),
          lt: z.number().optional(),
          lte: z.number().optional(),
        })
      ]).optional().describe('Filter by creation timestamp or range'),
      due_date: z.union([
        z.number(),
        z.object({
          gt: z.number().optional(),
          gte: z.number().optional(),
          lt: z.number().optional(),
          lte: z.number().optional(),
        })
      ]).optional()
    }),
    handler: async (client: StripeClient, args: any) => {
      return await client.list<Invoice>('/invoices', args);
    }
  },
  {
    name: 'stripe_get_invoice',
    description: 'Retrieve a specific invoice by ID',
    inputSchema: z.object({
      invoice_id: z.string().describe('The ID of the invoice to retrieve'),
      expand: z.array(z.string()).optional().describe('Fields to expand (e.g., ["customer", "subscription", "charge"])')
    }),
    handler: async (client: StripeClient, args: any) => {
      const { invoice_id, expand } = args;
      const params = expand ? { expand } : undefined;
      return await client.retrieve<Invoice>('/invoices', invoice_id, params);
    }
  },
  {
    name: 'stripe_create_invoice',
    description: 'Create a new draft invoice',
    inputSchema: z.object({
      customer: z.string().describe('Customer ID'),
      auto_advance: z.boolean().optional().describe('Auto-finalize after an hour (default true)'),
      collection_method: z.enum(['charge_automatically', 'send_invoice']).optional().describe('Collection method (default: charge_automatically)'),
      description: z.string().optional(),
      metadata: metadataSchema,
      subscription: z.string().optional().describe('Subscription ID to invoice'),
      days_until_due: z.number().optional().describe('Days until due (for send_invoice)'),
      due_date: z.number().optional().describe('Unix timestamp for due date'),
      default_payment_method: z.string().optional(),
      footer: z.string().optional().describe('Footer text'),
      statement_descriptor: z.string().optional(),
      automatic_tax: z.object({
        enabled: z.boolean()
      }).optional(),
      custom_fields: z.array(z.object({
        name: z.string(),
        value: z.string()
      })).optional().describe('Custom fields to display'),
      from_invoice: z.object({
        action: z.enum(['revision']),
        invoice: z.string()
      }).optional().describe('Create from existing invoice'),
      on_behalf_of: z.string().optional().describe('Connected account ID'),
      payment_settings: z.object({
        payment_method_types: z.array(z.string()).optional()
      }).optional(),
      rendering_options: z.object({
        amount_tax_display: z.enum(['include_inclusive_tax', 'exclude_inclusive_tax']).optional()
      }).optional()
    }),
    handler: async (client: StripeClient, args: any) => {
      return await client.create<Invoice>('/invoices', args, {
        idempotencyKey: client.generateIdempotencyKey()
      });
    }
  },
  {
    name: 'stripe_update_invoice',
    description: 'Update a draft invoice',
    inputSchema: z.object({
      invoice_id: z.string().describe('The ID of the invoice to update'),
      auto_advance: z.boolean().optional(),
      collection_method: z.enum(['charge_automatically', 'send_invoice']).optional(),
      description: z.string().optional(),
      metadata: metadataSchema,
      days_until_due: z.number().optional(),
      due_date: z.number().optional(),
      default_payment_method: z.string().optional(),
      footer: z.string().optional(),
      statement_descriptor: z.string().optional(),
      custom_fields: z.array(z.object({
        name: z.string(),
        value: z.string()
      })).optional(),
      payment_settings: z.object({
        payment_method_types: z.array(z.string()).optional()
      }).optional()
    }),
    handler: async (client: StripeClient, args: any) => {
      const { invoice_id, ...data } = args;
      return await client.update<Invoice>('/invoices', invoice_id, data);
    }
  },
  {
    name: 'stripe_finalize_invoice',
    description: 'Finalize a draft invoice',
    inputSchema: z.object({
      invoice_id: z.string().describe('The ID of the invoice to finalize'),
      auto_advance: z.boolean().optional().describe('Automatically charge or send')
    }),
    handler: async (client: StripeClient, args: any) => {
      const { invoice_id, ...data } = args;
      return await client.post<Invoice>(`/invoices/${invoice_id}/finalize`, data);
    }
  },
  {
    name: 'stripe_pay_invoice',
    description: 'Pay an invoice manually',
    inputSchema: z.object({
      invoice_id: z.string().describe('The ID of the invoice to pay'),
      payment_method: z.string().optional().describe('Payment method ID to use'),
      source: z.string().optional().describe('Payment source ID'),
      off_session: z.boolean().optional().describe('Whether payment is off-session'),
      paid_out_of_band: z.boolean().optional().describe('Mark as paid outside Stripe')
    }),
    handler: async (client: StripeClient, args: any) => {
      const { invoice_id, ...data } = args;
      return await client.post<Invoice>(`/invoices/${invoice_id}/pay`, data);
    }
  },
  {
    name: 'stripe_void_invoice',
    description: 'Void an invoice (mark as uncollectible)',
    inputSchema: z.object({
      invoice_id: z.string().describe('The ID of the invoice to void')
    }),
    handler: async (client: StripeClient, args: any) => {
      return await client.post<Invoice>(`/invoices/${args.invoice_id}/void`, {});
    }
  },
  {
    name: 'stripe_send_invoice',
    description: 'Send an invoice to the customer',
    inputSchema: z.object({
      invoice_id: z.string().describe('The ID of the invoice to send')
    }),
    handler: async (client: StripeClient, args: any) => {
      return await client.post<Invoice>(`/invoices/${args.invoice_id}/send`, {});
    }
  },
  {
    name: 'stripe_list_invoice_items',
    description: 'List invoice items (line items that will be added to next invoice)',
    inputSchema: z.object({
      limit: z.number().min(1).max(100).optional().describe('Number of items to return (1-100)'),
      starting_after: z.string().optional(),
      ending_before: z.string().optional(),
      customer: z.string().optional().describe('Filter by customer ID'),
      invoice: z.string().optional().describe('Filter by invoice ID'),
      pending: z.boolean().optional().describe('Only pending items (not yet on invoice)'),
      created: z.union([
        z.number(),
        z.object({
          gt: z.number().optional(),
          gte: z.number().optional(),
          lt: z.number().optional(),
          lte: z.number().optional(),
        })
      ]).optional()
    }),
    handler: async (client: StripeClient, args: any) => {
      return await client.list<InvoiceItem>('/invoiceitems', args);
    }
  },
  {
    name: 'stripe_create_invoice_item',
    description: 'Create an invoice item (one-time charge to be added to next invoice)',
    inputSchema: z.object({
      customer: z.string().describe('Customer ID'),
      amount: z.number().describe('Amount in cents (can be negative for discounts)'),
      currency: z.string().describe('Three-letter ISO currency code'),
      description: z.string().optional(),
      invoice: z.string().optional().describe('Invoice ID to add to (if not specified, added to next invoice)'),
      metadata: metadataSchema,
      price: z.string().optional().describe('Price ID to use'),
      quantity: z.number().optional().describe('Quantity (default 1)'),
      subscription: z.string().optional().describe('Subscription ID'),
      tax_rates: z.array(z.string()).optional().describe('Tax rate IDs'),
      unit_amount: z.number().optional().describe('Unit amount in cents (use with quantity)'),
      period: z.object({
        start: z.number(),
        end: z.number()
      }).optional().describe('Period for the item')
    }),
    handler: async (client: StripeClient, args: any) => {
      return await client.create<InvoiceItem>('/invoiceitems', args, {
        idempotencyKey: client.generateIdempotencyKey()
      });
    }
  },
  {
    name: 'stripe_delete_invoice_item',
    description: 'Delete an invoice item',
    inputSchema: z.object({
      invoice_item_id: z.string().describe('The ID of the invoice item to delete')
    }),
    handler: async (client: StripeClient, args: any) => {
      return await client.remove('/invoiceitems', args.invoice_item_id);
    }
  }
];
