/**
 * One-shot migration: legacy NocoDB Interests → new client/interest split.
 *
 * Usage:
 *
 *   pnpm tsx scripts/migrate-from-nocodb.ts --dry-run
 *     Pulls the live NocoDB base, runs the transform + dedup pipeline,
 *     writes a report to .migration/<timestamp>/. NO database writes.
 *
 *   pnpm tsx scripts/migrate-from-nocodb.ts --dry-run --port-slug port-nimara
 *     Same, but tags the planned writes with the named port (matters for
 *     the apply phase — every client/interest belongs to one port).
 *
 *   pnpm tsx scripts/migrate-from-nocodb.ts --apply --port-slug port-nimara
 *     Re-fetches NocoDB, re-transforms, then writes the planned rows
 *     into the target port via the idempotent `migration_source_links`
 *     ledger. Re-runs are safe — already-imported source IDs are skipped.
 *     REQUIRES `EMAIL_REDIRECT_TO` to be set in env (safety net) unless
 *     `--unsafe-skip-redirect-check` is also passed.
 *
 * Design reference: docs/superpowers/specs/2026-05-03-dedup-and-migration-design.md §9.
 */

import 'dotenv/config';
import { randomUUID } from 'node:crypto';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { eq } from 'drizzle-orm';

import { db } from '@/lib/db';
import { ports } from '@/lib/db/schema/ports';
import { SUPER_ADMIN_USER_ID } from '@/lib/db/seed-bootstrap';
import { applyPlan } from '@/lib/dedup/migration-apply';
import { fetchSnapshot, loadNocoDbConfig } from '@/lib/dedup/nocodb-source';
import { transformSnapshot } from '@/lib/dedup/migration-transform';
import { resolveReportPaths, writeReport } from '@/lib/dedup/migration-report';

interface CliArgs {
  dryRun: boolean;
  apply: boolean;
  portSlug: string | null;
  reportDir: string | null;
  unsafeSkipRedirectCheck: boolean;
}

function parseArgs(argv: string[]): CliArgs {
  const args: CliArgs = {
    dryRun: false,
    apply: false,
    portSlug: null,
    reportDir: null,
    unsafeSkipRedirectCheck: false,
  };
  for (let i = 0; i < argv.length; i += 1) {
    const a = argv[i]!;
    if (a === '--dry-run') args.dryRun = true;
    else if (a === '--apply') args.apply = true;
    else if (a === '--port-slug') args.portSlug = argv[++i] ?? null;
    else if (a === '--report') args.reportDir = argv[++i] ?? null;
    else if (a === '--unsafe-skip-redirect-check') args.unsafeSkipRedirectCheck = true;
    else if (a === '-h' || a === '--help') {
      printHelp();
      process.exit(0);
    } else {
      console.error(`Unknown argument: ${a}`);
      printHelp();
      process.exit(1);
    }
  }
  return args;
}

function printHelp(): void {
  console.log(`Usage:
  pnpm tsx scripts/migrate-from-nocodb.ts --dry-run [--port-slug <slug>]
    Pulls NocoDB → transforms → writes report to .migration/<timestamp>/.
    No database writes.

  pnpm tsx scripts/migrate-from-nocodb.ts --apply --port-slug <slug>
    Re-fetches NocoDB, re-transforms, writes via migration_source_links
    ledger. Idempotent — safe to re-run. Requires EMAIL_REDIRECT_TO set
    (unless --unsafe-skip-redirect-check is also passed).

Flags:
  --dry-run                       Read NocoDB, write report only.
  --apply                         Actually write rows to the DB.
  --port-slug <slug>              Port slug to attach to all imported
                                  entities. Defaults to the first
                                  available port if omitted.
  --report <dir>                  Path to a previously-generated report
                                  dir (only used by --apply).
  --unsafe-skip-redirect-check    Skip the EMAIL_REDIRECT_TO precondition
                                  check. Only use in production cutover.
  -h, --help                      Show this help.
`);
}

/**
 * Resolve the target port: use the slug if provided, otherwise the first
 * port found. Errors out cleanly if the slug doesn't match any port.
 */
async function resolvePort(slug: string | null): Promise<{ id: string; slug: string }> {
  if (slug) {
    const [p] = await db
      .select({ id: ports.id, slug: ports.slug })
      .from(ports)
      .where(eq(ports.slug, slug))
      .limit(1);
    if (!p) {
      console.error(`No port found with slug "${slug}".`);
      process.exit(1);
    }
    return { id: p.id, slug: p.slug };
  }
  const [first] = await db.select({ id: ports.id, slug: ports.slug }).from(ports).limit(1);
  if (!first) {
    console.error('No ports exist in the target DB. Seed at least one port before applying.');
    process.exit(1);
  }
  return { id: first.id, slug: first.slug };
}

async function main(): Promise<void> {
  const args = parseArgs(process.argv.slice(2));

  if (!args.dryRun && !args.apply) {
    console.error('Must specify --dry-run or --apply');
    printHelp();
    process.exit(1);
  }

  // Safety gate: --apply must run with EMAIL_REDIRECT_TO set, unless the
  // operator explicitly opts out (production cutover).
  if (args.apply && !process.env.EMAIL_REDIRECT_TO && !args.unsafeSkipRedirectCheck) {
    console.error(
      '--apply requires EMAIL_REDIRECT_TO to be set in the environment as a safety net.',
    );
    console.error('See docs/operations/outbound-comms-safety.md for the rationale.');
    console.error(
      'If you are running the production cutover and have read that doc, add ' +
        '--unsafe-skip-redirect-check to override.',
    );
    process.exit(2);
  }

  // ── Fetch + transform (shared by dry-run and apply) ──────────────────────

  console.log('[migrate] Loading NocoDB config…');
  const config = loadNocoDbConfig();
  console.log(`[migrate] Source: ${config.url}`);

  console.log('[migrate] Fetching snapshot from NocoDB…');
  const start = Date.now();
  const snapshot = await fetchSnapshot(config);
  const elapsed = ((Date.now() - start) / 1000).toFixed(1);
  console.log(
    `[migrate] Snapshot fetched in ${elapsed}s — ${snapshot.interests.length} interests, ${snapshot.residentialInterests.length} residential, ${snapshot.berths.length} berths, ${snapshot.expenses?.length ?? 0} expenses.`,
  );

  console.log('[migrate] Running transform + dedup pipeline…');
  const plan = transformSnapshot(snapshot);

  // Resolve output paths relative to the worktree root.
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
  const repoRoot = path.resolve(scriptDir, '..');
  const generatedAt = new Date().toISOString();
  const paths = resolveReportPaths(repoRoot);

  console.log(`[migrate] Writing report to ${paths.rootDir}…`);
  await writeReport(paths, plan, generatedAt);

  // ── Plan summary ─────────────────────────────────────────────────────────
  const s = plan.stats;
  console.log('');
  console.log('=== Migration Plan Summary ===');
  console.log(
    `  Input:     ${s.inputInterestRows} interests, ${s.inputResidentialRows} residential interests`,
  );
  console.log(`  Output:    ${s.outputClients} clients, ${s.outputInterests} interests`);
  console.log(`             ${s.outputContacts} contacts, ${s.outputAddresses} addresses`);
  console.log(
    `             ${s.outputDocuments} EOI documents, ${s.outputDocumentSigners} signers`,
  );
  console.log(
    `             ${s.outputResidentialClients} residential clients (with default-stage interests)`,
  );
  console.log(`             ${s.outputExpenses} expenses`);
  console.log(
    `  Dedup:     ${s.autoLinkedClusters} auto-linked clusters, ${s.needsReviewPairs} pairs flagged for review`,
  );
  console.log(`  Quality:   ${s.flaggedRows} rows flagged (see report.csv)`);
  console.log('');
  console.log(`  Full report: ${paths.summaryPath}`);

  if (args.dryRun) {
    console.log('');
    console.log('Dry-run complete. Re-run with --apply to write rows.');
    return;
  }

  // ── Apply path ───────────────────────────────────────────────────────────

  const port = await resolvePort(args.portSlug);
  const applyId = randomUUID();

  console.log('');
  console.log(`[migrate] Applying to port "${port.slug}" (id=${port.id})`);
  console.log(`[migrate] Apply id: ${applyId}`);
  console.log('[migrate] Inserting…');

  const applyStart = Date.now();
  const result = await applyPlan(plan, { port, applyId, appliedBy: SUPER_ADMIN_USER_ID });
  const applyElapsed = ((Date.now() - applyStart) / 1000).toFixed(1);

  console.log('');
  console.log('=== Apply Result ===');
  console.log(`  Time:      ${applyElapsed}s`);
  console.log(
    `  Clients:   ${result.clientsInserted} inserted, ${result.clientsSkipped} already linked`,
  );
  console.log(`  Contacts:  ${result.contactsInserted} inserted`);
  console.log(`  Addresses: ${result.addressesInserted} inserted`);
  console.log(`  Yachts:    ${result.yachtsInserted} inserted`);
  console.log(
    `  Interests: ${result.interestsInserted} inserted, ${result.interestsSkipped} already linked`,
  );
  console.log(
    `  Documents: ${result.documentsInserted} inserted, ${result.documentsSkipped} already linked`,
  );
  console.log(`  Signers:   ${result.documentSignersInserted} inserted`);
  console.log(
    `  Res-Clt:   ${result.residentialClientsInserted} inserted, ${result.residentialClientsSkipped} already linked`,
  );
  console.log(`  Res-Int:   ${result.residentialInterestsInserted} inserted`);
  console.log(
    `  Expenses:  ${result.expensesInserted} inserted, ${result.expensesSkipped} already linked`,
  );

  if (result.warnings.length > 0) {
    console.log('');
    console.log('Warnings:');
    for (const w of result.warnings.slice(0, 20)) {
      console.log(`  - ${w}`);
    }
    if (result.warnings.length > 20) {
      console.log(`  … ${result.warnings.length - 20} more`);
    }
  }

  // ── Multi-berth links (folded in for the one-shot seed) ──────────────────
  // The dedup plan only carries each deal's single `Berth Number`; the legacy
  // `_nc_m2m_Berths_Interests` junction (multi-berth deals) is reconnected
  // here from the local `nocodb_legacy` snapshot. Best-effort: if the dump
  // isn't restored, log + continue (the standalone script can run it later).
  try {
    const { connectBerthLinks } = await import('./migration/connect-berth-links');
    const bl = await connectBerthLinks({ portSlug: port.slug });
    console.log(
      `  Berths:    ${bl.inserted} multi-berth links inserted (${bl.madePrimary} new primary), ${bl.skipped} already linked`,
    );
    if (bl.unresolved.length > 0) {
      console.log(`             ⚠ ${bl.unresolved.length} moorings with no CRM berth`);
    }
  } catch (err) {
    console.log(
      `  Berths:    ⚠ multi-berth link step skipped (${(err as Error).message}). ` +
        `Run scripts/migration/connect-berth-links.ts once the nocodb_legacy dump is restored.`,
    );
  }
  console.log('');
}

main().catch((err) => {
  console.error('[migrate] Fatal error:', err);
  process.exit(1);
});
