import { getCollection } from '../lib/datastore.js';
import type { BaseDocument, DocumentCollection } from '@bytelyst/datastore';
import { MIGRATIONS } from './registry.js';
import type { MigrationDoc } from './types.js';

const CONTAINER = 'migrations';
const PRODUCT_ID = 'platform';

function collection(): DocumentCollection<MigrationDoc & BaseDocument> {
  return getCollection<MigrationDoc & BaseDocument>(CONTAINER, '/productId');
}

/**
 * Run all pending migrations in order. Idempotent — skips already-applied migrations.
 * Called on service startup. Failures are logged but do not block startup.
 */
export async function runPendingMigrations(): Promise<{
  applied: string[];
  skipped: string[];
  failed: string[];
}> {
  const applied: string[] = [];
  const skipped: string[] = [];
  const failed: string[] = [];

  // Fetch already-applied migrations
  let appliedVersions: Set<number>;
  try {
    const results = await collection().rawQuery<{ version: number }>(
      "SELECT c.version FROM c WHERE c.productId = @pid AND c.status = 'applied'",
      { pid: PRODUCT_ID }
    );
    appliedVersions = new Set(results.map(r => r.version));
  } catch {
    // Container may not exist yet — treat as empty
    appliedVersions = new Set();
  }

  for (const migration of MIGRATIONS) {
    if (appliedVersions.has(migration.version)) {
      skipped.push(migration.name);
      continue;
    }

    const startTime = Date.now();
    const doc = {
      id: migration.name,
      productId: PRODUCT_ID,
      version: migration.version,
      name: migration.name,
      description: migration.description,
      appliedAt: new Date().toISOString(),
      durationMs: 0,
      status: 'applied' as MigrationDoc['status'],
    };

    try {
      await migration.up();
      doc.durationMs = Date.now() - startTime;
      doc.status = 'applied';
      applied.push(migration.name);
    } catch (err) {
      doc.durationMs = Date.now() - startTime;
      doc.status = 'failed';
      (doc as MigrationDoc).error = err instanceof Error ? err.message : String(err);
      failed.push(migration.name);
      process.stderr.write(
        `[migrations] FAILED ${migration.name}: ${(doc as MigrationDoc).error}\n`
      );
    }

    // Record migration result (best-effort)
    try {
      await collection().upsert(doc as MigrationDoc & BaseDocument);
    } catch (recordErr) {
      process.stderr.write(
        `[migrations] Failed to record migration ${migration.name}: ${recordErr}\n`
      );
    }
  }

  if (applied.length > 0) {
    process.stdout.write(
      `[migrations] Applied ${applied.length} migration(s): ${applied.join(', ')}\n`
    );
  }
  if (skipped.length > 0) {
    process.stdout.write(`[migrations] Skipped ${skipped.length} already-applied migration(s)\n`);
  }

  return { applied, skipped, failed };
}

/**
 * List all migration records (for admin UI).
 */
export async function listMigrations(): Promise<MigrationDoc[]> {
  try {
    return await collection().rawQuery<MigrationDoc>(
      'SELECT * FROM c WHERE c.productId = @pid ORDER BY c.version ASC',
      { pid: PRODUCT_ID }
    );
  } catch {
    return [];
  }
}

/**
 * Get pending migrations that have not yet been applied.
 */
export function getPendingMigrations(appliedVersions: Set<number>) {
  return MIGRATIONS.filter(m => !appliedVersions.has(m.version)).map(m => ({
    version: m.version,
    name: m.name,
    description: m.description,
    reversible: m.reversible,
  }));
}
