import { z } from 'zod';
import { baseListQuerySchema } from '@/lib/api/list-query';
import { EXPENSE_CATEGORIES, PAYMENT_METHODS } from '@/lib/constants';

/**
 * Inner-shape ZodObject - kept exported (without .refine wrapping) so
 * `updateExpenseSchema` can still call `.partial()`. The `.refine()` rule
 * for "receipt or acknowledgement" is applied via `createExpenseSchema`.
 */
export const createExpenseShape = z.object({
  establishmentName: z.string().max(200).optional(),
  amount: z.coerce.number().positive(),
  currency: z.string().length(3).default('USD'),
  paymentMethod: z.enum(PAYMENT_METHODS).optional(),
  category: z.enum(EXPENSE_CATEGORIES).optional(),
  payer: z.string().max(200).optional(),
  expenseDate: z.coerce.date(),
  description: z.string().max(2000).optional(),
  receiptFileIds: z.array(z.string()).optional(),
  /**
   * Set to `true` when the rep deliberately creates an expense without a
   * receipt. The UI shows a non-blocking warning that surfaces both at
   * creation time and again in the PDF export. Without this flag, the
   * server rejects an expense submitted with no `receiptFileIds` so reps
   * can't accidentally lose-receipt by mistake.
   */
  noReceiptAcknowledged: z.boolean().optional().default(false),
  paymentStatus: z.enum(['unpaid', 'paid', 'partial']).default('unpaid'),
  paymentDate: z.string().optional(),
  paymentReference: z.string().optional(),
  paymentNotes: z.string().optional(),
  /**
   * Free-text trip / event label (e.g. "Palm Beach 2026"). Empty / null
   * means "no trip". The form's autocomplete suggests prior values per
   * port to keep spellings consistent so group-by works downstream.
   */
  tripLabel: z.string().max(120).optional(),
});

export const createExpenseSchema = createExpenseShape.refine(
  (v) => (v.receiptFileIds && v.receiptFileIds.length > 0) || v.noReceiptAcknowledged === true,
  {
    message:
      'Receipt required. Tick "I have no receipt for this expense" if you understand it may not be reimbursed.',
    path: ['receiptFileIds'],
  },
);

// Update accepts partial fields and skips the create-time receipt-or-ack
// rule (the row already exists and may legitimately be edited without
// touching receipts).
export const updateExpenseSchema = createExpenseShape.partial();

export const listExpensesSchema = baseListQuerySchema.extend({
  category: z.string().optional(),
  paymentStatus: z.string().optional(),
  dateFrom: z.string().optional(),
  dateTo: z.string().optional(),
  currency: z.string().optional(),
  payer: z.string().optional(),
  tripLabel: z.string().optional(),
});

/**
 * Body for `POST /api/v1/expenses/export/pdf`. Mirrors the legacy
 * `PDFOptions` shape from the Nuxt client-portal so reps can re-use the
 * same mental model. `expenseIds` selects an explicit subset; when
 * absent, the listExpenses-style filter is used to gather rows.
 *
 * Limits are deliberate:
 *   - max 1000 expenseIds so a runaway selection can't queue an OOM-able
 *     receipt-fetch loop (see expense-pdf.service.ts).
 *   - documentName is sanitized at the service layer for the filename;
 *     the validator only enforces a sane upper bound.
 */
export const exportExpensePdfSchema = z.object({
  expenseIds: z.array(z.string()).max(1000).optional(),
  filter: z
    .object({
      dateFrom: z.string().optional().nullable(),
      dateTo: z.string().optional().nullable(),
      category: z.string().optional().nullable(),
      paymentStatus: z.string().optional().nullable(),
      payer: z.string().optional().nullable(),
      tripLabel: z.string().optional().nullable(),
      includeArchived: z.boolean().optional(),
    })
    .optional(),
  options: z.object({
    documentName: z.string().min(1).max(200),
    subheader: z.string().max(300).optional(),
    groupBy: z.enum(['none', 'payer', 'category', 'date', 'trip']).default('none'),
    includeReceipts: z.boolean().default(false),
    includeReceiptContents: z.boolean().default(false),
    includeSummary: z.boolean().default(true),
    includeDetails: z.boolean().default(true),
    includeProcessingFee: z.boolean().default(false),
    targetCurrency: z.enum(['USD', 'EUR']).default('EUR'),
    pageFormat: z.enum(['A4', 'Letter', 'Legal']).default('A4'),
  }),
});

export type CreateExpenseInput = z.infer<typeof createExpenseSchema>;
export type UpdateExpenseInput = z.infer<typeof updateExpenseSchema>;
export type ListExpensesInput = z.infer<typeof listExpensesSchema>;
export type ExportExpensePdfInput = z.infer<typeof exportExpensePdfSchema>;
