import type { Database } from 'bun:sqlite';

import { LEGACY_SCHEMA_BUNDLE_MIGRATION } from './001_legacy-schema-bundle.js';
import { SCHEDULED_TASK_COLUMNS_MIGRATION } from './002_scheduled-task-columns.js';
import { ROOM_REGISTRATION_CANONICAL_COLUMNS_MIGRATION } from './003_room-registration-canonical-columns.js';
import { MESSAGE_SEQ_AND_WORK_ITEM_INDEXES_MIGRATION } from './004_message-seq-and-work-item-indexes.js';
import { SESSIONS_COMPOSITE_KEY_MIGRATION } from './005_sessions-composite-key.js';
import { CHAT_CHANNEL_METADATA_MIGRATION } from './006_chat-channel-metadata.js';
import { RUNTIME_SERVICE_METADATA_MIGRATION } from './007_runtime-service-metadata.js';
import { PAIRED_TASK_SCHEMA_CLEANUP_MIGRATION } from './008_paired-task-schema-cleanup.js';
import { PAIRED_WORKSPACE_PROJECT_SCHEMA_CLEANUP_MIGRATION } from './009_paired-workspace-project-schema-cleanup.js';
import { PAIRED_TURN_PROVENANCE_UPGRADE_MIGRATION } from './010_paired-turn-provenance-upgrade.js';
import { OWNER_FAILURE_COUNT_MIGRATION } from './011_owner-failure-count.js';
import { PAIRED_VERDICT_AND_STEP_TELEMETRY_MIGRATION } from './012_paired-verdict-and-step-telemetry.js';
import { MESSAGE_SOURCE_KIND_MIGRATION } from './013_message-source-kind.js';
import { WORK_ITEM_ATTACHMENTS_MIGRATION } from './014_work-item-attachments.js';
import { TURN_PROGRESS_TEXT_MIGRATION } from './015_turn-progress-text.js';
import { ROOM_SKILL_OVERRIDES_MIGRATION } from './016_room-skill-overrides.js';
import { SCHEDULED_TASK_ROOM_ROLE_MIGRATION } from './017_scheduled-task-room-role.js';
import { PAIRED_TURN_OUTPUT_ATTACHMENTS_MIGRATION } from './018_paired-turn-output-attachments.js';
import { TURN_PROGRESS_TEXT_RECOVERY_MIGRATION } from './019_turn-progress-text-recovery.js';
import { ARBITER_INTERVENTION_COUNT_MIGRATION } from './020_arbiter-intervention-count.js';
import { REVIEWER_FAILURE_COUNT_MIGRATION } from './021_reviewer-failure-count.js';
import { TURN_PROGRESS_TEXT_COMPAT_MIGRATION } from './022_turn-progress-text-compat.js';
import type {
  SchemaMigrationArgs,
  SchemaMigrationDefinition,
} from './types.js';

const SCHEMA_MIGRATIONS_TABLE = 'schema_migrations';

const ORDERED_SCHEMA_MIGRATIONS: readonly SchemaMigrationDefinition[] = [
  LEGACY_SCHEMA_BUNDLE_MIGRATION,
  SCHEDULED_TASK_COLUMNS_MIGRATION,
  ROOM_REGISTRATION_CANONICAL_COLUMNS_MIGRATION,
  MESSAGE_SEQ_AND_WORK_ITEM_INDEXES_MIGRATION,
  SESSIONS_COMPOSITE_KEY_MIGRATION,
  CHAT_CHANNEL_METADATA_MIGRATION,
  RUNTIME_SERVICE_METADATA_MIGRATION,
  PAIRED_TASK_SCHEMA_CLEANUP_MIGRATION,
  PAIRED_WORKSPACE_PROJECT_SCHEMA_CLEANUP_MIGRATION,
  PAIRED_TURN_PROVENANCE_UPGRADE_MIGRATION,
  OWNER_FAILURE_COUNT_MIGRATION,
  PAIRED_VERDICT_AND_STEP_TELEMETRY_MIGRATION,
  MESSAGE_SOURCE_KIND_MIGRATION,
  WORK_ITEM_ATTACHMENTS_MIGRATION,
  TURN_PROGRESS_TEXT_MIGRATION,
  ROOM_SKILL_OVERRIDES_MIGRATION,
  SCHEDULED_TASK_ROOM_ROLE_MIGRATION,
  PAIRED_TURN_OUTPUT_ATTACHMENTS_MIGRATION,
  TURN_PROGRESS_TEXT_RECOVERY_MIGRATION,
  ARBITER_INTERVENTION_COUNT_MIGRATION,
  REVIEWER_FAILURE_COUNT_MIGRATION,
  TURN_PROGRESS_TEXT_COMPAT_MIGRATION,
];

function ensureSchemaMigrationsTable(database: Database): void {
  database.exec(`
    CREATE TABLE IF NOT EXISTS ${SCHEMA_MIGRATIONS_TABLE} (
      version INTEGER PRIMARY KEY,
      name TEXT NOT NULL,
      applied_at TEXT NOT NULL DEFAULT (datetime('now'))
    )
  `);
}

function getAppliedSchemaVersions(database: Database): Set<number> {
  const rows = database
    .prepare(
      `SELECT version
         FROM ${SCHEMA_MIGRATIONS_TABLE}
        ORDER BY version ASC`,
    )
    .all() as Array<{ version: number }>;
  return new Set(rows.map((row) => row.version));
}

function recordAppliedSchemaMigration(
  database: Database,
  migration: SchemaMigrationDefinition,
): void {
  database
    .prepare(
      `INSERT INTO ${SCHEMA_MIGRATIONS_TABLE} (version, name)
       VALUES (?, ?)`,
    )
    .run(migration.version, migration.name);
}

export function applyVersionedSchemaMigrations(
  database: Database,
  args: SchemaMigrationArgs,
): void {
  ensureSchemaMigrationsTable(database);
  const appliedVersions = getAppliedSchemaVersions(database);

  for (const migration of ORDERED_SCHEMA_MIGRATIONS) {
    const alreadyApplied = appliedVersions.has(migration.version);
    if (alreadyApplied && !migration.alwaysRun) {
      continue;
    }

    // Fail fast: a partially-applied migration must not be recorded as complete.
    migration.apply(database, args);
    if (!alreadyApplied) {
      recordAppliedSchemaMigration(database, migration);
    }
  }
}
