/**
 * Phase B - operational insight surfaces.
 *
 * - `alerts`: rule-engine-fired actionable cards. The fingerprint column
 *   dedupes re-evaluations of the same condition; the partial unique
 *   index keeps a single open row per `(port, fingerprint)` while
 *   resolved/dismissed history accumulates.
 * - `analytics_snapshots`: cached aggregate JSON keyed by metric+range,
 *   refreshed by a recurring job so dashboard hits don't recompute.
 */

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

import { ports } from './ports';
import { user } from './users';

export const alerts = pgTable(
  'alerts',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id, { onDelete: 'cascade' }),
    /** Stable rule identifier: 'reservation.no_agreement', 'interest.stale', ... */
    ruleId: text('rule_id').notNull(),
    /** 'info' | 'warning' | 'critical' */
    severity: text('severity').notNull(),
    title: text('title').notNull(),
    body: text('body'),
    /** Relative path the card deep-links to. */
    link: text('link').notNull(),
    /** Optional FK target: 'interest', 'reservation', 'document', 'expense', ... */
    entityType: text('entity_type'),
    entityId: text('entity_id'),
    /** Hash of (rule_id + entity_type + entity_id) - dedupes re-evaluations. */
    fingerprint: text('fingerprint').notNull(),
    firedAt: timestamp('fired_at', { withTimezone: true }).notNull().defaultNow(),
    dismissedAt: timestamp('dismissed_at', { withTimezone: true }),
    dismissedBy: text('dismissed_by').references(() => user.id),
    /** "Someone is on it" - alert stays visible but stops nagging. */
    acknowledgedAt: timestamp('acknowledged_at', { withTimezone: true }),
    acknowledgedBy: text('acknowledged_by').references(() => user.id),
    /** Set by the engine when the underlying condition no longer fires. */
    resolvedAt: timestamp('resolved_at', { withTimezone: true }),
    /** Per-rule extras: days_stale, amount_at_risk, etc. */
    metadata: jsonb('metadata').default({}),
  },
  (table) => [
    // Only one open alert per (port, fingerprint) - re-evaluation upserts.
    uniqueIndex('idx_alerts_fingerprint_open')
      .on(table.portId, table.fingerprint)
      .where(sql`resolved_at IS NULL`),
    index('idx_alerts_port_fired').on(table.portId, table.firedAt),
    index('idx_alerts_port_severity_open')
      .on(table.portId, table.severity)
      .where(sql`resolved_at IS NULL AND dismissed_at IS NULL`),
  ],
);

export type Alert = typeof alerts.$inferSelect;
export type NewAlert = typeof alerts.$inferInsert;

export const analyticsSnapshots = pgTable(
  'analytics_snapshots',
  {
    portId: text('port_id')
      .notNull()
      .references(() => ports.id, { onDelete: 'cascade' }),
    /** Composite key: e.g. 'pipeline_funnel.30d', 'occupancy_timeline.90d'. */
    metricId: text('metric_id').notNull(),
    computedAt: timestamp('computed_at', { withTimezone: true }).notNull().defaultNow(),
    /** Pre-shaped chart data. */
    data: jsonb('data').notNull(),
  },
  (table) => [uniqueIndex('idx_analytics_pk').on(table.portId, table.metricId)],
);

export type AnalyticsSnapshot = typeof analyticsSnapshots.$inferSelect;
export type NewAnalyticsSnapshot = typeof analyticsSnapshots.$inferInsert;

/** Severity literal type for callers that want a typed enum. */
export type AlertSeverity = 'info' | 'warning' | 'critical';

/**
 * Rule IDs in the v1 catalog - keep in sync with `alert-rules.ts`.
 *
 * Two rules from the original spec (`document.expiring_soon`,
 * `audit.suspicious_login`) are deferred until their data sources land:
 *  - `document.expiring_soon` needs a `documents.expires_at` column populated
 *    from Documenso responses (currently expiry isn't tracked).
 *  - `audit.suspicious_login` needs better-auth instrumentation that writes
 *    `login.failed` audit rows (the auth layer currently doesn't).
 * Re-add the literal here + the evaluator in `alert-rules.ts` once their
 * dependencies ship.
 */
export const ALERT_RULES = [
  'reservation.no_agreement',
  'interest.stale',
  'interest.no_activity',
  'document.signer_overdue',
  'berth.under_offer_stalled',
  'expense.duplicate',
  'expense.unscanned',
  'interest.high_value_silent',
  'eoi.unsigned_long',
] as const;

export type AlertRuleId = (typeof ALERT_RULES)[number];
