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

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

/**
 * Saved report templates. Each row captures a named, port-scoped
 * configuration that backs the "Saved templates" dropdown in the
 * Export-as-PDF dialog. The `config` JSONB matches the discriminated-
 * union shape the route schema enforces, so reusing a template is a
 * straight pass-through to /api/v1/reports/generate.
 *
 * Migration: 0079_report_templates.sql.
 */
export const reportTemplates = pgTable(
  'report_templates',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id, { onDelete: 'cascade' }),
    /** Mirrors the discriminator on ReportConfig - 'dashboard' |
     *  'clients' | 'berths' | 'interests'. Validated at the route
     *  layer. */
    kind: text('kind').notNull(),
    name: text('name').notNull(),
    description: text('description'),
    /** Untyped at the Drizzle layer; route + service layers validate
     *  via the same zod schemas they use on `/api/v1/reports/generate`. */
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    config: jsonb('config').$type<Record<string, any>>().notNull(),
    /** 'private' = creator only; 'team' = anyone with reports.export on
     *  this port. Locked decision: skip "shared with specific users"
     *  beyond port-wide team for v1. */
    visibility: text('visibility').notNull().default('private'),
    archivedAt: timestamp('archived_at', { withTimezone: true }),
    createdBy: text('created_by').notNull(),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    index('idx_report_templates_port').on(table.portId),
    index('idx_report_templates_port_kind').on(table.portId, table.kind),
    index('idx_report_templates_port_visibility').on(table.portId, table.visibility),
    // Sibling-name uniqueness per port+kind. The lower() expression in
    // the SQL migration mirrors how case-insensitive collisions are
    // handled elsewhere in the schema. Migration 0084 adds a WHERE clause
    // to allow archived templates to free up their names.
    uniqueIndex('uniq_report_templates_port_kind_name').on(
      table.portId,
      table.kind,
      sql`LOWER(${table.name})`,
    ),
  ],
);

export type ReportTemplate = typeof reportTemplates.$inferSelect;
export type NewReportTemplate = typeof reportTemplates.$inferInsert;

// ─── report_schedules ──────────────────────────────────────────────────────
/**
 * Recurring schedule that re-runs a saved template on a fixed cadence + emails
 * the resulting artefact to a defined recipient list. Pre-computes
 * `next_run_at` so the BullMQ scheduler can poll `enabled=true AND next_run_at <= now()`
 * efficiently. Cadence enum at v1; cron strings can layer later.
 *
 * Migration: 0084_reports_page.sql.
 */
export const reportSchedules = pgTable(
  'report_schedules',
  {
    id: text('id')
      .primaryKey()
      .default(sql`gen_random_uuid()::text`),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id, { onDelete: 'cascade' }),
    templateId: text('template_id')
      .notNull()
      .references(() => reportTemplates.id, { onDelete: 'cascade' }),
    /** 'weekly_monday_9' | 'monthly_first_9' | 'quarterly_first_9' for v1. */
    cadence: text('cadence').notNull(),
    recipients: jsonb('recipients').$type<Array<{ name?: string; email: string }>>().notNull(),
    outputFormat: text('output_format').notNull().default('pdf'),
    enabled: boolean('enabled').notNull().default(true),
    lastRunAt: timestamp('last_run_at', { withTimezone: true }),
    nextRunAt: timestamp('next_run_at', { withTimezone: true }).notNull(),
    createdBy: text('created_by')
      .notNull()
      .references(() => user.id, { onDelete: 'restrict' }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    index('report_schedules_port_enabled_next_idx').on(
      table.portId,
      table.enabled,
      table.nextRunAt,
    ),
  ],
);

export type ReportSchedule = typeof reportSchedules.$inferSelect;
export type NewReportSchedule = typeof reportSchedules.$inferInsert;

// ─── report_runs ───────────────────────────────────────────────────────────
/**
 * Append-only audit log of every generated report. Snapshots the config
 * used so re-runs reproduce identically even if the source template has
 * been edited or archived.
 *
 * Migration: 0084_reports_page.sql.
 */
export const reportRuns = pgTable(
  'report_runs',
  {
    id: text('id')
      .primaryKey()
      .default(sql`gen_random_uuid()::text`),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id, { onDelete: 'cascade' }),
    templateId: text('template_id').references(() => reportTemplates.id, { onDelete: 'set null' }),
    scheduleId: text('schedule_id').references(() => reportSchedules.id, { onDelete: 'set null' }),
    kind: text('kind').notNull(),
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    config: jsonb('config').$type<Record<string, any>>().notNull(),
    outputFormat: text('output_format').notNull(),
    storageKey: text('storage_key'),
    sizeBytes: integer('size_bytes'),
    /** 'pending' | 'rendering' | 'complete' | 'failed'. */
    status: text('status').notNull().default('pending'),
    errorMessage: text('error_message'),
    /** 'user' | 'schedule'. */
    triggeredBy: text('triggered_by').notNull(),
    triggeredByUserId: text('triggered_by_user_id').references(() => user.id, {
      onDelete: 'set null',
    }),
    emailedTo: jsonb('emailed_to').$type<Array<{ name?: string; email: string }>>(),
    emailedAt: timestamp('emailed_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    completedAt: timestamp('completed_at', { withTimezone: true }),
  },
  (table) => [
    index('report_runs_port_created_idx').on(table.portId, table.createdAt),
    index('report_runs_port_user_idx').on(table.portId, table.triggeredByUserId),
    index('report_runs_port_template_idx').on(table.portId, table.templateId),
  ],
);

export type ReportRun = typeof reportRuns.$inferSelect;
export type NewReportRun = typeof reportRuns.$inferInsert;
