import { pgTable, text, jsonb, timestamp, index, uniqueIndex } from 'drizzle-orm/pg-core';

import { ports } from './ports';

/**
 * Raw capture of every website inquiry submission, dual-written from the
 * marketing site alongside its existing NocoDB write. Acts as a passive
 * collector while the website still uses NocoDB as its primary system of
 * record - the new CRM observes incoming traffic without altering it,
 * letting us validate the data flow before any cutover.
 *
 * v1 deliberately stores the raw payload as JSON without promoting to
 * `clients` / `interests` rows. Once we trust the pipeline, a separate
 * "promote" job can transform these submissions into proper entities
 * with full dedup / merge logic.
 *
 * Idempotency: each submission carries a `submission_id` UUID minted by
 * the website's server. Re-delivery (network retry, double-click) hits
 * the unique index and is treated as a no-op.
 */
export const websiteSubmissions = pgTable(
  'website_submissions',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    /** Multi-tenant: every submission belongs to a port. Resolved from
     *  `port_slug` in the request payload (defaults to port-nimara if
     *  unspecified, since it's currently the only port with a public
     *  marketing site). */
    portId: text('port_id')
      .notNull()
      .references(() => ports.id),
    /** UUID v4 minted by the website's server when the form is processed.
     *  The unique index on this column enforces idempotency: retries from
     *  the website resolve to the existing row instead of creating a
     *  duplicate. */
    submissionId: text('submission_id').notNull(),
    /** Discriminator for the form type. Mirrors the website's existing
     *  branches (`berth_inquiry` from /api/register?interest=berths,
     *  `residence_inquiry` from /api/register?interest=residences,
     *  `contact_form` from /api/contact). Add new kinds as the website
     *  grows new form types. */
    kind: text('kind').notNull(),
    /** Verbatim form payload, including any reCAPTCHA / IP / user-agent
     *  metadata the website chose to forward. Stored as JSONB so the
     *  capture stays schema-flexible while we figure out which fields
     *  matter for the eventual promote step. */
    payload: jsonb('payload').notNull(),
    /** Cross-reference back to the legacy NocoDB row id created by the
     *  same form submission. Useful for reconciling: pick any submission
     *  here, look up the matching NocoDB row, confirm both halves agree. */
    legacyNocodbId: text('legacy_nocodb_id'),
    /** Contact name + email extracted from `payload` at capture time so the
     *  inquiry list can search/sort/display via real columns (payload stays
     *  JSONB and isn't searched directly). Populated by the capture endpoint
     *  and backfilled in migration 0093. */
    contactName: text('contact_name'),
    contactEmail: text('contact_email'),
    /** Set when an operator converts this inquiry into CRM entities. FK enforced
     *  at the DB level (migration 0093); typed as plain text here to avoid a
     *  circular schema import — `clients`/`interests` already reference
     *  `website_submissions`. */
    convertedClientId: text('converted_client_id'),
    convertedInterestId: text('converted_interest_id'),
    /** Capture-time metadata for debugging. */
    sourceIp: text('source_ip'),
    userAgent: text('user_agent'),
    /** UTM attribution columns. Opportunistic — populated when the
     *  marketing site's tracker pulled them out of the query string or
     *  the referrer. Indexed jointly with port_id + received_at via
     *  migration 0089 for fast per-campaign rollups. */
    utmSource: text('utm_source'),
    utmMedium: text('utm_medium'),
    utmCampaign: text('utm_campaign'),
    utmTerm: text('utm_term'),
    utmContent: text('utm_content'),
    receivedAt: timestamp('received_at', { withTimezone: true }).notNull().defaultNow(),
    /** Triage workflow state. Default 'open'; transitions to
     *  'converted' (operator created a client/interest from this row),
     *  'dismissed' (operator marked as not actionable), or 'assigned'
     *  (operator opened it but hasn't resolved yet). The inbox default
     *  query filters to open + assigned. */
    triageState: text('triage_state').notNull().default('open'),
    triagedAt: timestamp('triaged_at', { withTimezone: true }),
    /** better-auth user id of the operator who last changed triage_state. */
    triagedBy: text('triaged_by'),
  },
  (table) => [
    uniqueIndex('idx_ws_submission_id').on(table.submissionId),
    index('idx_ws_port_received').on(table.portId, table.receivedAt),
    index('idx_ws_kind').on(table.kind),
    index('idx_ws_triage_state').on(table.portId, table.triageState, table.receivedAt),
    index('idx_ws_utm_source').on(table.portId, table.utmSource, table.receivedAt),
  ],
);

export type WebsiteSubmission = typeof websiteSubmissions.$inferSelect;
export type NewWebsiteSubmission = typeof websiteSubmissions.$inferInsert;
