/**
 * Media maintenance (PLANNING.md §3, §6c, §7). Run over SSH on the host.
 *
 *   npm run reprocess-media           regenerate every derivative from the stored
 *                                     originals (after a codec/quality change)
 *   npm run reprocess-media -- --gc   hard-delete images soft-deleted > 30 days
 *                                     ago and remove their derivative directories
 */
import { db, destroyDb } from '../src/lib/db';
import { generateDerivatives, normalise } from '../src/modules/media/derivative.service';
import { removeImageDir, resolveOriginal } from '../src/modules/media/storage.service';
import { replaceVariants, type NewVariantRow } from '../src/modules/media/media.repository';

interface Row {
  id: string;
  page: 'genesis' | 'hive';
  content_hash: string;
}

async function reprocessAll(): Promise<void> {
  const rows = await db<Row>('media_images')
    .select('id', 'page', 'content_hash')
    .whereNull('deleted_at')
    .where({ status: 'ready' });

  let ok = 0;
  let skipped = 0;
  for (const row of rows) {
    const original = await resolveOriginal(row.content_hash);
    if (!original) {
      console.warn(`skip ${row.id} — original ${row.content_hash} not found`);
      skipped += 1;
      continue;
    }
    const norm = await normalise(original);
    const specs = await generateDerivatives(norm, row.page, row.content_hash);
    const variantRows: NewVariantRow[] = specs.map((s) => ({
      image_id: row.id,
      format: s.format,
      width: s.width,
      height: s.height,
      bytes: s.bytes,
      path: s.path,
      is_primary: s.isPrimary ? 1 : 0,
    }));
    await db.transaction(async (trx) => {
      await replaceVariants(row.id, variantRows, trx);
      await trx('media_images')
        .where({ id: row.id })
        .update({ width: norm.width, height: norm.height });
    });
    ok += 1;
  }
  console.log(`reprocess: ${ok} regenerated, ${skipped} skipped`);
}

async function gc(): Promise<void> {
  const stale = await db<Row>('media_images')
    .select('id', 'page', 'content_hash')
    .whereNotNull('deleted_at')
    .where('deleted_at', '<', db.raw('NOW() - INTERVAL 30 DAY'));

  for (const row of stale) {
    await removeImageDir(row.page, row.content_hash);
    await db('media_images').where({ id: row.id }).del(); // media_variants cascades
  }
  console.log(`gc: removed ${stale.length} images soft-deleted > 30 days ago`);
}

async function main(): Promise<void> {
  if (process.argv.includes('--gc')) await gc();
  else await reprocessAll();
  await destroyDb();
}

void main().catch(async (err) => {
  console.error(err);
  await destroyDb();
  process.exit(1);
});
