import {
  pgTable,
  text,
  boolean,
  numeric,
  integer,
  timestamp,
  date,
  index,
  uniqueIndex,
  primaryKey,
  jsonb,
  AnyPgColumn,
} from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
import { ports } from './ports';
import { files } from './documents';
import { interests } from './interests';

export const expenses = pgTable(
  'expenses',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id),
    establishmentName: text('establishment_name'),
    amount: numeric('amount').notNull(),
    currency: text('currency').notNull().default('USD'),
    amountUsd: numeric('amount_usd'),
    exchangeRate: numeric('exchange_rate'),
    paymentMethod: text('payment_method'),
    category: text('category'),
    payer: text('payer'),
    expenseDate: timestamp('expense_date', { withTimezone: true }).notNull(),
    description: text('description'),
    receiptFileIds: text('receipt_file_ids').array(), // references to files table
    /**
     * True when the rep deliberately created the expense WITHOUT a receipt
     * (e.g. the receipt was lost or never issued). Surfaces a warning at
     * creation time AND in the PDF export - the legacy parent-company flow
     * may refuse to reimburse expenses without proof, so the warning is
     * load-bearing for ops.
     */
    noReceiptAcknowledged: boolean('no_receipt_acknowledged').notNull().default(false),
    paymentStatus: text('payment_status').default('unpaid'), // unpaid, paid, partial
    paymentDate: date('payment_date'),
    paymentReference: text('payment_reference'),
    paymentNotes: text('payment_notes'),
    /**
     * Free-text trip / event label so reps can group expenses for one
     * yacht show or business trip (e.g. "Palm Beach 2026"). Deliberately
     * un-normalized - events are 6–12/year and full event-management
     * functionality lives outside this CRM. The autocomplete on the
     * expense form keeps spellings consistent so group-by works.
     */
    tripLabel: text('trip_label'),
    /** When set, this expense is flagged as a duplicate of another in the
     *  same port. Self-referencing FK; the dedup service writes this. */
    duplicateOf: text('duplicate_of').references((): AnyPgColumn => expenses.id, {
      onDelete: 'set null',
    }),
    /** Last time the dedup heuristic ran against this row. */
    dedupScannedAt: timestamp('dedup_scanned_at', { withTimezone: true }),
    /** OCR pipeline state: 'pending'|'ok'|'failed'|'low_confidence'. */
    ocrStatus: text('ocr_status').default('pending'),
    /** Full Claude Vision response payload for audit/debug. */
    ocrRaw: jsonb('ocr_raw'),
    /** 0..1; values < 0.6 force the verify-mode UI. */
    ocrConfidence: numeric('ocr_confidence'),
    createdBy: text('created_by').notNull(),
    archivedAt: timestamp('archived_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    index('idx_expenses_port').on(table.portId),
    index('idx_expenses_date').on(table.portId, table.expenseDate),
    index('idx_expenses_category').on(table.portId, table.category),
    // Powers the dedup heuristic lookup (port + vendor + amount + date window).
    index('idx_expenses_dedup')
      .on(table.portId, table.establishmentName, table.amount, table.expenseDate)
      .where(sql`duplicate_of IS NULL`),
    // Powers the autocomplete + group-by-trip filter / PDF section.
    index('idx_expenses_trip_label').on(table.portId, table.tripLabel),
  ],
);

export const invoices = pgTable(
  'invoices',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id),
    invoiceNumber: text('invoice_number').notNull(), // INV-YYYYMM-### auto-generated
    clientName: text('client_name').notNull(),
    billingEntityType: text('billing_entity_type').notNull().default('client'), // 'client' | 'company'
    billingEntityId: text('billing_entity_id').notNull().default(''),
    billingEmail: text('billing_email'),
    billingAddress: text('billing_address'),
    dueDate: date('due_date').notNull(),
    paymentTerms: text('payment_terms').notNull().default('net30'), // immediate, net10, net15, net30, net45, net60
    currency: text('currency').notNull().default('USD'),
    subtotal: numeric('subtotal').notNull(),
    discountPct: numeric('discount_pct').default('0'),
    discountAmount: numeric('discount_amount').default('0'),
    feePct: numeric('fee_pct').default('0'),
    feeAmount: numeric('fee_amount').default('0'),
    total: numeric('total').notNull(),
    status: text('status').notNull().default('draft'), // draft, sent, paid, overdue, cancelled
    paymentStatus: text('payment_status').default('unpaid'),
    paymentDate: date('payment_date'),
    paymentMethod: text('payment_method'),
    paymentReference: text('payment_reference'),
    // H-01: nullable - losing the rendered invoice PDF shouldn't bring
    // down the invoice row (totals + payments are the source of truth).
    pdfFileId: text('pdf_file_id').references(() => files.id, { onDelete: 'set null' }),
    /** Optional link to a sales interest. When the invoice is paid and `kind`
     *  is 'deposit', recordPayment auto-advances the interest's pipelineStage
     *  to deposit_paid (no-op if already further along). */
    interestId: text('interest_id').references(() => interests.id, { onDelete: 'set null' }),
    /** Invoice kind. 'general' (default) is everyday billing; 'deposit' marks
     *  the 10% berth-purchase deposit and is what triggers the stage advance. */
    kind: text('kind').notNull().default('general'), // 'general' | 'deposit'
    notes: text('notes'),
    createdBy: text('created_by').notNull(),
    archivedAt: timestamp('archived_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    uniqueIndex('idx_invoices_number').on(table.portId, table.invoiceNumber),
    index('idx_invoices_port').on(table.portId),
    index('idx_invoices_status').on(table.portId, table.status),
    index('idx_invoices_billing_entity').on(
      table.portId,
      table.billingEntityType,
      table.billingEntityId,
    ),
    index('idx_invoices_interest').on(table.portId, table.interestId),
  ],
);

export const invoiceLineItems = pgTable(
  'invoice_line_items',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    invoiceId: text('invoice_id')
      .notNull()
      .references(() => invoices.id, { onDelete: 'cascade' }),
    description: text('description').notNull(),
    quantity: numeric('quantity').notNull().default('1'),
    unitPrice: numeric('unit_price').notNull(),
    total: numeric('total').notNull(),
    sortOrder: integer('sort_order').notNull().default(0),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [index('idx_ili_invoice').on(table.invoiceId)],
);

export const invoiceExpenses = pgTable(
  'invoice_expenses',
  {
    invoiceId: text('invoice_id')
      .notNull()
      .references(() => invoices.id, { onDelete: 'cascade' }),
    expenseId: text('expense_id')
      .notNull()
      .references(() => expenses.id, { onDelete: 'cascade' }),
  },
  (table) => [primaryKey({ columns: [table.invoiceId, table.expenseId] })],
);

export type Expense = typeof expenses.$inferSelect;
export type NewExpense = typeof expenses.$inferInsert;
export type Invoice = typeof invoices.$inferSelect;
export type NewInvoice = typeof invoices.$inferInsert;
export type InvoiceLineItem = typeof invoiceLineItems.$inferSelect;
export type NewInvoiceLineItem = typeof invoiceLineItems.$inferInsert;
export type InvoiceExpense = typeof invoiceExpenses.$inferSelect;
export type NewInvoiceExpense = typeof invoiceExpenses.$inferInsert;
