/**
 * AUDIT-2 #3 smoke — prisma migrate deploy actually has migrations to
 * apply.
 *
 * Background (third-party audit, 2026-05-23):
 *   docker-entrypoint.sh runs `npx prisma migrate deploy`, but the
 *   repo had ZERO proper Prisma migrations — every schema change
 *   lived in prisma/legacy-manual-sql/*.sql, which Prisma's `deploy`
 *   command does not read. So `deploy` always printed "No pending
 *   migrations to apply" and exited 0, while the app booted
 *   expecting columns that did not exist (P2022 on first write).
 *   The "AUDIT-10 migrate deploy" guard from PR #47 was theatre.
 *
 * Fix:
 *   All 14 manual SQL files re-encoded as proper Prisma migrations
 *   under prisma/migrations/<timestamp>_<name>/migration.sql, plus
 *   migration_lock.toml at the directory root. docs/runbooks/
 *   prisma-migrations.md documents the post-merge
 *   `prisma migrate resolve --applied` operator step.
 *
 * This smoke statically asserts:
 *   1. prisma/migrations/migration_lock.toml exists with mysql provider.
 *   2. At least 14 timestamped migration directories exist, each with
 *      a migration.sql file.
 *   3. The legacy prisma/legacy-manual-sql/ directory still exists
 *      with the README marking it historical-only.
 *   4. docker-entrypoint.sh still runs `prisma migrate deploy`.
 *   5. docs/runbooks/prisma-migrations.md exists with the resolve
 *      commands for the 14 historical migrations.
 */
import './_lib';
import { ok, fail } from './_lib';
import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
import path from 'path';

const ROOT = path.resolve(__dirname, '../..');
const MIGRATIONS_DIR = path.join(ROOT, 'prisma/migrations');
const MIGRATION_LOCK = path.join(MIGRATIONS_DIR, 'migration_lock.toml');
// audit3-followup (2026-05-30): relocated out of prisma/migrations/ — a folder
// there without migration.sql makes `prisma migrate deploy` fail P3015. Kept as
// historical reference at prisma/legacy-manual-sql/.
const MANUAL_DIR = path.join(ROOT, 'prisma/legacy-manual-sql');
const MANUAL_README = path.join(MANUAL_DIR, 'README.md');
const ENTRYPOINT = path.join(ROOT, 'docker-entrypoint.sh');
const RUNBOOK = path.join(ROOT, 'docs/runbooks/prisma-migrations.md');

function read(rel: string, abs: string): string {
    try {
        return readFileSync(abs, 'utf8');
    } catch (err) {
        fail(`read ${rel}`, String(err));
    }
}

function assertPresent(label: string, src: string, needle: string | RegExp): void {
    const re = typeof needle === 'string'
        ? new RegExp(needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
        : needle;
    if (!re.test(src)) {
        fail(label, `missing required pattern: ${String(needle)}`);
    }
    ok(label);
}

function main(): void {
    // 1. migration_lock.toml exists and declares mysql.
    if (!existsSync(MIGRATION_LOCK)) {
        fail('audit2-3: migration_lock.toml present', `not found at ${MIGRATION_LOCK}`);
    }
    const lock = read('migration_lock.toml', MIGRATION_LOCK);
    assertPresent('audit2-3: migration_lock declares mysql provider', lock, /provider\s*=\s*['"]mysql['"]/);

    // 2. Count timestamped migration directories.
    const entries = readdirSync(MIGRATIONS_DIR);
    const TIMESTAMP_RE = /^\d{14}_/;
    const migrationDirs = entries.filter((name) => {
        if (!TIMESTAMP_RE.test(name)) return false;
        return statSync(path.join(MIGRATIONS_DIR, name)).isDirectory();
    });
    if (migrationDirs.length < 14) {
        fail(
            'audit2-3: at least 14 Prisma migration directories present',
            `found ${migrationDirs.length}, expected >=14`,
        );
    }
    ok('audit2-3: at least 14 Prisma migration directories present', `${migrationDirs.length} found`);

    // 3. Each migration directory contains a migration.sql file.
    const missing = migrationDirs.filter((d) => !existsSync(path.join(MIGRATIONS_DIR, d, 'migration.sql')));
    if (missing.length > 0) {
        fail('audit2-3: every migration dir has migration.sql', `missing in: ${missing.join(', ')}`);
    }
    ok('audit2-3: every migration dir has migration.sql');

    // 4. manual/ kept for reference, with README explaining status.
    if (!existsSync(MANUAL_DIR)) {
        fail('audit2-3: manual/ directory preserved', `not found at ${MANUAL_DIR}`);
    }
    ok('audit2-3: manual/ directory preserved (historical reference)');

    if (!existsSync(MANUAL_README)) {
        fail('audit2-3: manual/README.md explains historical status', `not found at ${MANUAL_README}`);
    }
    const manualReadme = read('manual/README.md', MANUAL_README);
    assertPresent('audit2-3: manual/README warns against new files', manualReadme, /Do not add new files here/i);

    // 5. docker-entrypoint.sh still runs migrate deploy (the fix is in
    // the migrations being read, not the command).
    const entrypoint = read('docker-entrypoint.sh', ENTRYPOINT);
    assertPresent('audit2-3: entrypoint runs prisma migrate deploy', entrypoint, /prisma migrate deploy/);

    // 6. Runbook exists with the resolve commands.
    if (!existsSync(RUNBOOK)) {
        fail('audit2-3: docs/runbooks/prisma-migrations.md exists', `not found at ${RUNBOOK}`);
    }
    const runbook = read('docs/runbooks/prisma-migrations.md', RUNBOOK);
    assertPresent('audit2-3: runbook documents migrate resolve --applied', runbook, /prisma migrate resolve --applied/);

    // Verify the runbook lists every migration directory by name. If a
    // future PR adds a migration but forgets to update the runbook, the
    // operator's resolve list becomes incomplete on legacy DBs.
    for (const dir of migrationDirs) {
        if (!runbook.includes(dir)) {
            fail(
                'audit2-3: runbook lists every historical migration',
                `runbook missing entry for ${dir}`,
            );
        }
    }
    ok('audit2-3: runbook lists every historical migration', `${migrationDirs.length} entries`);

    ok('audit2-3-prisma-migrate-real', 'prisma migrate deploy now has migrations to apply; AUDIT-10 entrypoint is no longer theatre');
}

main();
