/**
 * Fix-up: connect the multi-berth links the main dedup pipeline misses.
 *
 * The dedup pipeline migrates only each interest's single `Berth Number` text
 * field; the legacy `_nc_m2m_Berths_Interests` junction (multi-berth deals) is
 * not carried over by it. This reads that junction from the `nocodb_legacy`
 * snapshot, resolves each legacy interest → its migrated interest (via the
 * ledger) and each mooring → the migrated berth, and inserts the missing
 * `interest_berths` rows.
 *
 * Idempotent: `ON CONFLICT (interest_id, berth_id) DO NOTHING`. Primary safety:
 * only makes a berth primary when the interest has no primary yet (≤1 primary
 * per interest is a partial unique index).
 *
 * Exposed as `connectBerthLinks(...)` so `migrate-from-nocodb.ts --apply` can
 * fold it into the one-shot seed; also runnable standalone:
 *
 *   pnpm tsx scripts/migration/connect-berth-links.ts [--port-slug port-nimara] [--dry-run]
 */
import 'dotenv/config';
import { randomUUID } from 'node:crypto';
import postgres from 'postgres';

const canonMoo = (raw: string): string => {
  const m = /^([A-Za-z]+)-?0*(\d+)$/.exec((raw ?? '').trim());
  return m ? `${m[1]!.toUpperCase()}${parseInt(m[2]!, 10)}` : (raw ?? '').trim();
};

export interface ConnectBerthLinksResult {
  inserted: number;
  madePrimary: number;
  skipped: number;
  unresolved: string[];
}

/**
 * Self-contained: opens its own CRM + legacy connections (read-only on the
 * legacy snapshot), does the work, closes them, returns stats. Safe to call
 * from the runner or standalone.
 */
export async function connectBerthLinks(opts: {
  portSlug?: string;
  dryRun?: boolean;
}): Promise<ConnectBerthLinksResult> {
  const slug = opts.portSlug ?? 'port-nimara';
  const dry = opts.dryRun ?? false;

  const CRM_URL = process.env.DATABASE_URL!;
  const LEGACY_URL = process.env.LEGACY_DB_URL ?? CRM_URL.replace(/\/[^/]+$/, '/nocodb_legacy');
  const crm = postgres(CRM_URL, { max: 4 });
  const legacy = postgres(LEGACY_URL, { max: 4 });

  try {
    const [port] = await crm`select id from ports where slug=${slug} limit 1`;
    if (!port) throw new Error(`no port ${slug}`);
    const portId = port.id as string;

    // legacy junction: interestId → set(moorings)
    const mooById = new Map<number, string>();
    for (const b of await legacy`select id, "Mooring_Number" m from plplouets5zw1um."Berths"`)
      mooById.set(b.id as number, canonMoo(b.m as string));
    const legacyMoo = new Map<number, Set<string>>();
    for (const j of await legacy`select "Interests_id" i, "Berths_id" b from plplouets5zw1um."_nc_m2m_Berths_Interests"`) {
      const set = legacyMoo.get(j.i as number) ?? new Set<string>();
      const m = mooById.get(j.b as number);
      if (m) set.add(m);
      legacyMoo.set(j.i as number, set);
    }
    // EOI-signed flag per legacy interest (for is_in_eoi_bundle)
    const signed = new Set<number>();
    for (const r of await legacy`select id, "EOI_Status" e, "LOI_NDA_Document" l from plplouets5zw1um."Interests"`) {
      const e = ((r.e as string) ?? '').trim();
      const l = ((r.l as string) ?? '').trim();
      if (
        e === 'Signed' ||
        ['Signing Complete', 'Signed by Client', 'Signed by Developer'].includes(l)
      )
        signed.add(r.id as number);
    }

    // ledger: legacy interest id → new interest id
    const links =
      await crm`select source_id, target_entity_id from migration_source_links where source_system='nocodb_interests' and target_entity_type='interest'`;
    const newInterestBySrc = new Map(
      links.map((l) => [Number(l.source_id), l.target_entity_id as string]),
    );

    // CRM berth id by mooring (this port)
    const berthByMoo = new Map(
      (await crm`select id, mooring_number m from berths where port_id=${portId}`).map((b) => [
        b.m as string,
        b.id as string,
      ]),
    );

    let inserted = 0;
    let madePrimary = 0;
    let skipped = 0;
    const unresolved: string[] = [];

    for (const [legacyId, moorings] of legacyMoo) {
      const interestId = newInterestBySrc.get(legacyId);
      if (!interestId) continue; // not a migrated interest (backup/copy tables)
      const primaryCheck =
        await crm`select exists(select 1 from interest_berths where interest_id=${interestId} and is_primary) as has`;
      let hasPrimary = (primaryCheck[0]?.has as boolean | undefined) ?? false;

      for (const moo of moorings) {
        const berthId = berthByMoo.get(moo);
        if (!berthId) {
          unresolved.push(`${legacyId}:${moo}`);
          continue;
        }
        const makePrimary = !hasPrimary;
        if (dry) {
          inserted++;
          if (makePrimary) {
            madePrimary++;
            hasPrimary = true;
          }
          continue;
        }
        const res = await crm`
          insert into interest_berths (id, interest_id, berth_id, is_primary, is_specific_interest, is_in_eoi_bundle)
          values (${randomUUID()}, ${interestId}, ${berthId}, ${makePrimary}, true, ${signed.has(legacyId)})
          on conflict (interest_id, berth_id) do nothing
          returning id`;
        if (res.length > 0) {
          inserted++;
          if (makePrimary) {
            madePrimary++;
            hasPrimary = true;
          }
        } else {
          skipped++;
        }
      }
    }

    return { inserted, madePrimary, skipped, unresolved };
  } finally {
    await crm.end().catch(() => {});
    await legacy.end().catch(() => {});
  }
}

// ─── Standalone CLI ──────────────────────────────────────────────────────────
function isMain(): boolean {
  const arg = process.argv[1] ?? '';
  return arg.includes('connect-berth-links');
}

if (isMain()) {
  const slugArg = (() => {
    const i = process.argv.indexOf('--port-slug');
    return i >= 0 ? (process.argv[i + 1] ?? 'port-nimara') : 'port-nimara';
  })();
  const dry = process.argv.includes('--dry-run');

  connectBerthLinks({ portSlug: slugArg, dryRun: dry })
    .then((r) => {
      console.log(
        `connect-berth-links ${dry ? '(DRY)' : ''}: inserted ${r.inserted} links (${r.madePrimary} new primary), ${r.skipped} already linked`,
      );
      if (r.unresolved.length)
        console.log(
          `  ⚠ ${r.unresolved.length} moorings with no CRM berth: ${r.unresolved.slice(0, 20).join(', ')}`,
        );
      process.exit(0);
    })
    .catch((e) => {
      console.error('connect-berth-links failed:', e);
      process.exit(1);
    });
}
