import {
  pgTable,
  text,
  boolean,
  integer,
  numeric,
  timestamp,
  jsonb,
  index,
  uniqueIndex,
  customType,
} from 'drizzle-orm/pg-core';
import { ports } from './ports';
import { clients } from './clients';

// Drizzle doesn't ship a first-class tsvector type; declare a thin custom one.
const tsvector = customType<{ data: string; driverData: string }>({
  dataType() {
    return 'tsvector';
  },
});

export const auditLogs = pgTable(
  'audit_logs',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id').references(() => ports.id), // null for system-level events
    userId: text('user_id'), // null for system-generated events
    action: text('action').notNull(), // create, update, delete, archive, restore, merge, login, logout, revert
    entityType: text('entity_type').notNull(), // client, interest, berth, expense, invoice, file, user, role, etc.
    entityId: text('entity_id'),
    fieldChanged: text('field_changed'),
    oldValue: jsonb('old_value'),
    newValue: jsonb('new_value'),
    ipAddress: text('ip_address'),
    userAgent: text('user_agent'),
    revertedBy: text('reverted_by'), // user ID if this change was reverted
    revertedAt: timestamp('reverted_at', { withTimezone: true }),
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    revertOf: text('revert_of').references((): any => auditLogs.id),
    metadata: jsonb('metadata').default({}),
    /** Full-text search column. Stored generated; updated by the migration's
     *  GENERATED ALWAYS expression covering action + entityType + entityId
     *  + actor email lookup. */
    searchText: tsvector('search_text'),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    index('idx_al_port').on(table.portId, table.createdAt),
    index('idx_al_entity').on(table.entityType, table.entityId),
    index('idx_al_user').on(table.userId, table.createdAt),
    index('idx_al_created').on(table.createdAt),
  ],
);

export const tags = pgTable(
  'tags',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id),
    name: text('name').notNull(),
    color: text('color').notNull().default('#6B7280'),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    uniqueIndex('tags_port_name_idx').on(table.portId, table.name),
    index('idx_tags_port').on(table.portId),
  ],
);

export const webhooks = pgTable(
  'webhooks',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id),
    name: text('name').notNull(),
    url: text('url').notNull(),
    secret: text('secret'),
    events: text('events').array().notNull(),
    isActive: boolean('is_active').notNull().default(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_webhooks_port').on(table.portId)],
);

export const webhookDeliveries = pgTable(
  'webhook_deliveries',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    webhookId: text('webhook_id')
      .notNull()
      .references(() => webhooks.id, { onDelete: 'cascade' }),
    eventType: text('event_type').notNull(),
    payload: jsonb('payload').notNull(),
    responseStatus: integer('response_status'),
    responseBody: text('response_body'),
    attempt: integer('attempt').notNull().default(1),
    status: text('status').notNull().default('pending'), // pending, success, failed, dead_letter
    deliveredAt: timestamp('delivered_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [index('idx_wd_webhook').on(table.webhookId, table.createdAt)],
);

export const systemSettings = pgTable(
  'system_settings',
  {
    key: text('key').notNull(),
    value: jsonb('value').notNull(),
    portId: text('port_id').references(() => ports.id), // null for global settings
    updatedBy: text('updated_by'),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    uniqueIndex('system_settings_key_port_idx').on(table.key, table.portId),
    // Note: the PRIMARY KEY is `key` alone based on schema, but unique on (key, port_id)
    // We use key as primary key per SQL schema
  ],
);

export const savedViews = pgTable(
  'saved_views',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id),
    userId: text('user_id').notNull(),
    entityType: text('entity_type').notNull(), // clients, interests, berths, expenses, invoices
    name: text('name').notNull(),
    filters: jsonb('filters').notNull(),
    sortConfig: jsonb('sort_config'),
    columnConfig: jsonb('column_config'),
    isShared: boolean('is_shared').notNull().default(false),
    isDefault: boolean('is_default').notNull().default(false),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [index('idx_sv_user').on(table.userId, table.entityType)],
);

export const scratchpadNotes = pgTable(
  'scratchpad_notes',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    userId: text('user_id').notNull(),
    content: text('content').notNull(),
    linkedClientId: text('linked_client_id').references(() => clients.id),
    linkedAt: timestamp('linked_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [index('idx_sp_user').on(table.userId)],
);

export const userNotificationPreferences = pgTable(
  'user_notification_preferences',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    userId: text('user_id').notNull(),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id),
    notificationType: text('notification_type').notNull(),
    inApp: boolean('in_app').notNull().default(true),
    email: boolean('email').notNull().default(true),
  },
  (table) => [
    uniqueIndex('unp_user_port_type_idx').on(table.userId, table.portId, table.notificationType),
  ],
);

export const currencyRates = pgTable(
  'currency_rates',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    baseCurrency: text('base_currency').notNull(),
    targetCurrency: text('target_currency').notNull(),
    rate: numeric('rate').notNull(),
    source: text('source').notNull().default('frankfurter'), // frankfurter, manual
    fetchedAt: timestamp('fetched_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    uniqueIndex('currency_rates_base_target_idx').on(table.baseCurrency, table.targetCurrency),
  ],
);

export const customFieldDefinitions = pgTable(
  'custom_field_definitions',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    portId: text('port_id')
      .notNull()
      .references(() => ports.id),
    entityType: text('entity_type').notNull(), // client, interest, berth
    fieldName: text('field_name').notNull(),
    fieldLabel: text('field_label').notNull(),
    fieldType: text('field_type').notNull(), // text, number, date, boolean, select
    selectOptions: jsonb('select_options'), // for select type: ["option1", "option2"]
    isRequired: boolean('is_required').notNull().default(false),
    sortOrder: integer('sort_order').notNull().default(0),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    uniqueIndex('cfd_port_entity_name_idx').on(table.portId, table.entityType, table.fieldName),
    index('idx_cfd_port').on(table.portId),
  ],
);

export const customFieldValues = pgTable(
  'custom_field_values',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => crypto.randomUUID()),
    fieldId: text('field_id')
      .notNull()
      .references(() => customFieldDefinitions.id, { onDelete: 'cascade' }),
    entityId: text('entity_id').notNull(), // references the client/interest/berth ID
    value: jsonb('value').notNull(),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (table) => [
    uniqueIndex('cfv_field_entity_idx').on(table.fieldId, table.entityId),
    index('idx_cfv_entity').on(table.entityId),
  ],
);

export type AuditLog = typeof auditLogs.$inferSelect;
export type NewAuditLog = typeof auditLogs.$inferInsert;
export type Tag = typeof tags.$inferSelect;
export type NewTag = typeof tags.$inferInsert;
export type Webhook = typeof webhooks.$inferSelect;
export type NewWebhook = typeof webhooks.$inferInsert;
export type WebhookDelivery = typeof webhookDeliveries.$inferSelect;
export type NewWebhookDelivery = typeof webhookDeliveries.$inferInsert;
export type SystemSetting = typeof systemSettings.$inferSelect;
export type NewSystemSetting = typeof systemSettings.$inferInsert;
export type SavedView = typeof savedViews.$inferSelect;
export type NewSavedView = typeof savedViews.$inferInsert;
export type ScratchpadNote = typeof scratchpadNotes.$inferSelect;
export type NewScratchpadNote = typeof scratchpadNotes.$inferInsert;
export type UserNotificationPreference = typeof userNotificationPreferences.$inferSelect;
export type NewUserNotificationPreference = typeof userNotificationPreferences.$inferInsert;
export type CurrencyRate = typeof currencyRates.$inferSelect;
export type NewCurrencyRate = typeof currencyRates.$inferInsert;
export type CustomFieldDefinition = typeof customFieldDefinitions.$inferSelect;
export type NewCustomFieldDefinition = typeof customFieldDefinitions.$inferInsert;
export type CustomFieldValue = typeof customFieldValues.$inferSelect;
export type NewCustomFieldValue = typeof customFieldValues.$inferInsert;
