/**
 * Synthetic industrial listing seed (TEC-2769 — R5.2.2).
 *
 * Generates a distribution-matched labelled dataset of industrial listings for
 * listing-level AVM training. Riding on top of the 12 hand-curated rows in
 * `seed-industrial-listings.ts`, this script produces ~600 additional rows
 * stratified across region × propertyType × leaseType × park.
 *
 * ## Provenance / licensing
 *
 * - All rows are SYNTHETIC. No rows are scraped, copied, or derived from any
 *   third-party listings platform or transaction feed.
 * - No PII. Sellers and agents reuse existing seeded profiles
 *   (`seed-seller-001`, `seed-seller-002`, `seed-agentprofile-00{1,2,3}`).
 * - Generator is deterministic given `SYNTH_SEED` (default `2026`). Re-running
 *   with the same seed reproduces the same rows via upsert-on-id.
 *
 * ## Usage
 *
 *     # As part of `pnpm db:seed` (automatic)
 *     pnpm db:seed
 *
 *     # Standalone
 *     npx tsx scripts/seed-industrial-listings-synth.ts
 *
 *     # Override generator seed
 *     SYNTH_SEED=42 npx tsx scripts/seed-industrial-listings-synth.ts
 *
 * Idempotent: upserts by id (`synth-ind-listing-0001`..`synth-ind-listing-0600`).
 */

import { PrismaPg } from '@prisma/adapter-pg';
import {
  IndustrialLeaseType,
  IndustrialListingStatus,
  IndustrialPropertyType,
  PrismaClient,
  VietnamRegion,
} from '@prisma/client';
import pg from 'pg';

// ---------------------------------------------------------------------------
// Deterministic RNG (mulberry32) — same seed → same dataset.
// ---------------------------------------------------------------------------
function mulberry32(seedNumber: number): () => number {
  let state = seedNumber >>> 0;
  return () => {
    state = (state + 0x6d2b79f5) >>> 0;
    let t = state;
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

const DEFAULT_SEED = 2026;
const SEED = Number(process.env['SYNTH_SEED'] ?? DEFAULT_SEED);
const rng = mulberry32(SEED);

function rand(): number {
  return rng();
}
function randRange(min: number, max: number): number {
  return min + rand() * (max - min);
}
function randInt(min: number, max: number): number {
  return Math.floor(randRange(min, max + 1));
}
function pickWeighted<T>(items: readonly (readonly [T, number])[]): T {
  const total = items.reduce((s, [, w]) => s + w, 0);
  let r = rand() * total;
  for (const [item, w] of items) {
    r -= w;
    if (r <= 0) return item;
  }
  return items[items.length - 1]![0];
}
function roundTo(value: number, decimals: number): number {
  const f = 10 ** decimals;
  return Math.round(value * f) / f;
}

// ---------------------------------------------------------------------------
// Reference data: the 20 seeded parks, with minimal context the generator
// needs. Must stay in sync with `scripts/seed-industrial-parks.ts`.
// ---------------------------------------------------------------------------
interface ParkRef {
  id: string;
  region: VietnamRegion;
  landRentUsdM2Year: number | null;
  rbfRentUsdM2Month: number | null;
  rbwRentUsdM2Month: number | null;
  managementFeeUsd: number | null;
}

const PARKS: ParkRef[] = [
  { id: 'seed-kcn-001', region: VietnamRegion.NORTH, landRentUsdM2Year: 90, rbfRentUsdM2Month: 5.5, rbwRentUsdM2Month: 4.8, managementFeeUsd: 0.7 },
  { id: 'seed-kcn-002', region: VietnamRegion.SOUTH, landRentUsdM2Year: 180, rbfRentUsdM2Month: 6.5, rbwRentUsdM2Month: 5.8, managementFeeUsd: 0.8 },
  { id: 'seed-kcn-003', region: VietnamRegion.SOUTH, landRentUsdM2Year: 130, rbfRentUsdM2Month: 4.8, rbwRentUsdM2Month: 4.2, managementFeeUsd: 0.65 },
  { id: 'seed-kcn-004', region: VietnamRegion.SOUTH, landRentUsdM2Year: 110, rbfRentUsdM2Month: 4.5, rbwRentUsdM2Month: 4.0, managementFeeUsd: 0.6 },
  { id: 'seed-kcn-005', region: VietnamRegion.NORTH, landRentUsdM2Year: 100, rbfRentUsdM2Month: 4.8, rbwRentUsdM2Month: 4.2, managementFeeUsd: 0.6 },
  { id: 'seed-kcn-006', region: VietnamRegion.SOUTH, landRentUsdM2Year: 140, rbfRentUsdM2Month: 5.0, rbwRentUsdM2Month: 4.5, managementFeeUsd: 0.5 },
  { id: 'seed-kcn-007', region: VietnamRegion.SOUTH, landRentUsdM2Year: 150, rbfRentUsdM2Month: 5.5, rbwRentUsdM2Month: 4.8, managementFeeUsd: 0.7 },
  { id: 'seed-kcn-008', region: VietnamRegion.NORTH, landRentUsdM2Year: 85, rbfRentUsdM2Month: 4.5, rbwRentUsdM2Month: 4.0, managementFeeUsd: 0.55 },
  { id: 'seed-kcn-009', region: VietnamRegion.SOUTH, landRentUsdM2Year: 95, rbfRentUsdM2Month: 4.3, rbwRentUsdM2Month: 3.9, managementFeeUsd: 0.55 },
  { id: 'seed-kcn-010', region: VietnamRegion.SOUTH, landRentUsdM2Year: 120, rbfRentUsdM2Month: 4.8, rbwRentUsdM2Month: 4.2, managementFeeUsd: 0.6 },
  { id: 'seed-kcn-011', region: VietnamRegion.NORTH, landRentUsdM2Year: 95, rbfRentUsdM2Month: 4.5, rbwRentUsdM2Month: 4.0, managementFeeUsd: 0.6 },
  { id: 'seed-kcn-012', region: VietnamRegion.NORTH, landRentUsdM2Year: 85, rbfRentUsdM2Month: 4.3, rbwRentUsdM2Month: 3.8, managementFeeUsd: 0.6 },
  { id: 'seed-kcn-013', region: VietnamRegion.SOUTH, landRentUsdM2Year: 100, rbfRentUsdM2Month: 4.2, rbwRentUsdM2Month: 3.7, managementFeeUsd: 0.5 },
  { id: 'seed-kcn-014', region: VietnamRegion.SOUTH, landRentUsdM2Year: 80, rbfRentUsdM2Month: 4.0, rbwRentUsdM2Month: 3.6, managementFeeUsd: 0.5 },
  { id: 'seed-kcn-015', region: VietnamRegion.NORTH, landRentUsdM2Year: 110, rbfRentUsdM2Month: 5.0, rbwRentUsdM2Month: 4.5, managementFeeUsd: 0.65 },
  { id: 'seed-kcn-016', region: VietnamRegion.NORTH, landRentUsdM2Year: 105, rbfRentUsdM2Month: 4.5, rbwRentUsdM2Month: 4.0, managementFeeUsd: 0.6 },
  { id: 'seed-kcn-017', region: VietnamRegion.SOUTH, landRentUsdM2Year: 95, rbfRentUsdM2Month: 4.8, rbwRentUsdM2Month: 4.2, managementFeeUsd: 0.55 },
  { id: 'seed-kcn-018', region: VietnamRegion.SOUTH, landRentUsdM2Year: 110, rbfRentUsdM2Month: 4.5, rbwRentUsdM2Month: 4.0, managementFeeUsd: 0.55 },
  { id: 'seed-kcn-019', region: VietnamRegion.CENTRAL, landRentUsdM2Year: 60, rbfRentUsdM2Month: 3.8, rbwRentUsdM2Month: 3.4, managementFeeUsd: 0.4 },
  { id: 'seed-kcn-020', region: VietnamRegion.CENTRAL, landRentUsdM2Year: 40, rbfRentUsdM2Month: 3.5, rbwRentUsdM2Month: 3.2, managementFeeUsd: 0.35 },
];

const PARKS_BY_REGION: Record<VietnamRegion, ParkRef[]> = {
  [VietnamRegion.NORTH]: PARKS.filter((p) => p.region === VietnamRegion.NORTH),
  [VietnamRegion.SOUTH]: PARKS.filter((p) => p.region === VietnamRegion.SOUTH),
  [VietnamRegion.CENTRAL]: PARKS.filter((p) => p.region === VietnamRegion.CENTRAL),
};

// ---------------------------------------------------------------------------
// Stratification table: region × propertyType → row count.
// Totals to 600 synthetic rows.
// ---------------------------------------------------------------------------
type StratKey = { region: VietnamRegion; propertyType: IndustrialPropertyType };
const STRATIFICATION: ReadonlyArray<StratKey & { count: number }> = [
  // INDUSTRIAL_LAND
  { region: VietnamRegion.NORTH,   propertyType: IndustrialPropertyType.INDUSTRIAL_LAND,       count: 40 },
  { region: VietnamRegion.SOUTH,   propertyType: IndustrialPropertyType.INDUSTRIAL_LAND,       count: 55 },
  { region: VietnamRegion.CENTRAL, propertyType: IndustrialPropertyType.INDUSTRIAL_LAND,       count: 15 },
  // READY_BUILT_FACTORY
  { region: VietnamRegion.NORTH,   propertyType: IndustrialPropertyType.READY_BUILT_FACTORY,   count: 65 },
  { region: VietnamRegion.SOUTH,   propertyType: IndustrialPropertyType.READY_BUILT_FACTORY,   count: 90 },
  { region: VietnamRegion.CENTRAL, propertyType: IndustrialPropertyType.READY_BUILT_FACTORY,   count: 15 },
  // READY_BUILT_WAREHOUSE
  { region: VietnamRegion.NORTH,   propertyType: IndustrialPropertyType.READY_BUILT_WAREHOUSE, count: 55 },
  { region: VietnamRegion.SOUTH,   propertyType: IndustrialPropertyType.READY_BUILT_WAREHOUSE, count: 80 },
  { region: VietnamRegion.CENTRAL, propertyType: IndustrialPropertyType.READY_BUILT_WAREHOUSE, count: 15 },
  // LOGISTICS_CENTER
  { region: VietnamRegion.NORTH,   propertyType: IndustrialPropertyType.LOGISTICS_CENTER,      count: 30 },
  { region: VietnamRegion.SOUTH,   propertyType: IndustrialPropertyType.LOGISTICS_CENTER,      count: 45 },
  { region: VietnamRegion.CENTRAL, propertyType: IndustrialPropertyType.LOGISTICS_CENTER,      count: 10 },
  // OFFICE_IN_PARK
  { region: VietnamRegion.NORTH,   propertyType: IndustrialPropertyType.OFFICE_IN_PARK,        count: 25 },
  { region: VietnamRegion.SOUTH,   propertyType: IndustrialPropertyType.OFFICE_IN_PARK,        count: 35 },
  { region: VietnamRegion.CENTRAL, propertyType: IndustrialPropertyType.OFFICE_IN_PARK,        count: 10 },
  // DATA_CENTER
  { region: VietnamRegion.NORTH,   propertyType: IndustrialPropertyType.DATA_CENTER,           count: 10 },
  { region: VietnamRegion.SOUTH,   propertyType: IndustrialPropertyType.DATA_CENTER,           count: 15 },
  { region: VietnamRegion.CENTRAL, propertyType: IndustrialPropertyType.DATA_CENTER,           count:  5 },
];

const TOTAL_EXPECTED = STRATIFICATION.reduce((s, x) => s + x.count, 0);

// ---------------------------------------------------------------------------
// Feature bounds per propertyType.
// ---------------------------------------------------------------------------
interface FeatureBounds {
  areaM2: [number, number];
  ceilingHeightM: [number, number] | null;
  floorLoadTonM2: [number, number] | null;
  columnSpacingM: [number, number] | null;
  dockCount: [number, number] | null;
  craneChance: number;
  craneCapacityTon: [number, number] | null;
  mezzanineChance: number;
  officeChance: number;
  officeFraction: [number, number]; // fraction of areaM2 used as office
  powerCapacityKva: [number, number] | null;
  waterSupplyM3Day: [number, number] | null;
  pricingMode: 'land' | 'rbf' | 'rbw' | 'office' | 'data_center';
  priceBias: number; // multiplier applied to park-anchored price
}

const BOUNDS: Record<IndustrialPropertyType, FeatureBounds> = {
  [IndustrialPropertyType.INDUSTRIAL_LAND]: {
    areaM2: [2_000, 50_000],
    ceilingHeightM: null,
    floorLoadTonM2: null,
    columnSpacingM: null,
    dockCount: null,
    craneChance: 0,
    craneCapacityTon: null,
    mezzanineChance: 0,
    officeChance: 0,
    officeFraction: [0, 0],
    powerCapacityKva: null,
    waterSupplyM3Day: null,
    pricingMode: 'land',
    priceBias: 1.0,
  },
  [IndustrialPropertyType.READY_BUILT_FACTORY]: {
    areaM2: [800, 12_000],
    ceilingHeightM: [8, 14],
    floorLoadTonM2: [2, 5],
    columnSpacingM: [10, 20],
    dockCount: [2, 8],
    craneChance: 0.4,
    craneCapacityTon: [3, 15],
    mezzanineChance: 0.45,
    officeChance: 0.8,
    officeFraction: [0.03, 0.1],
    powerCapacityKva: [200, 2_000],
    waterSupplyM3Day: [20, 120],
    pricingMode: 'rbf',
    priceBias: 1.0,
  },
  [IndustrialPropertyType.READY_BUILT_WAREHOUSE]: {
    areaM2: [500, 20_000],
    ceilingHeightM: [9, 14],
    floorLoadTonM2: [3, 6],
    columnSpacingM: [10, 20],
    dockCount: [2, 12],
    craneChance: 0.15,
    craneCapacityTon: [3, 10],
    mezzanineChance: 0.2,
    officeChance: 0.6,
    officeFraction: [0.02, 0.06],
    powerCapacityKva: [100, 600],
    waterSupplyM3Day: [10, 80],
    pricingMode: 'rbw',
    priceBias: 1.0,
  },
  [IndustrialPropertyType.LOGISTICS_CENTER]: {
    areaM2: [4_000, 30_000],
    ceilingHeightM: [10, 14],
    floorLoadTonM2: [3, 6],
    columnSpacingM: [12, 22],
    dockCount: [6, 20],
    craneChance: 0.05,
    craneCapacityTon: [3, 8],
    mezzanineChance: 0.1,
    officeChance: 0.7,
    officeFraction: [0.02, 0.05],
    powerCapacityKva: [300, 1_500],
    waterSupplyM3Day: [30, 120],
    pricingMode: 'rbw',
    priceBias: 0.95,
  },
  [IndustrialPropertyType.OFFICE_IN_PARK]: {
    areaM2: [200, 1_500],
    ceilingHeightM: [3, 4.5],
    floorLoadTonM2: null,
    columnSpacingM: null,
    dockCount: null,
    craneChance: 0,
    craneCapacityTon: null,
    mezzanineChance: 0,
    officeChance: 1,
    officeFraction: [1, 1],
    powerCapacityKva: [60, 200],
    waterSupplyM3Day: [3, 10],
    pricingMode: 'office',
    priceBias: 1.5,
  },
  [IndustrialPropertyType.DATA_CENTER]: {
    areaM2: [1_500, 10_000],
    ceilingHeightM: [4, 6],
    floorLoadTonM2: [5, 10],
    columnSpacingM: [9, 15],
    dockCount: [0, 2],
    craneChance: 0.05,
    craneCapacityTon: [2, 5],
    mezzanineChance: 0.1,
    officeChance: 0.9,
    officeFraction: [0.05, 0.15],
    powerCapacityKva: [1_500, 8_000],
    waterSupplyM3Day: [40, 200],
    pricingMode: 'rbf',
    priceBias: 1.3,
  },
};

// ---------------------------------------------------------------------------
// leaseType distribution per propertyType (sublease bias applied to price).
// ---------------------------------------------------------------------------
const LEASE_TYPE_PROBS: Record<
  IndustrialPropertyType,
  ReadonlyArray<readonly [IndustrialLeaseType, number]>
> = {
  [IndustrialPropertyType.INDUSTRIAL_LAND]: [
    [IndustrialLeaseType.LAND_LEASE, 1.0],
  ],
  [IndustrialPropertyType.READY_BUILT_FACTORY]: [
    [IndustrialLeaseType.FACTORY_LEASE, 0.9],
    [IndustrialLeaseType.SUBLEASE, 0.1],
  ],
  [IndustrialPropertyType.READY_BUILT_WAREHOUSE]: [
    [IndustrialLeaseType.WAREHOUSE_LEASE, 0.85],
    [IndustrialLeaseType.SUBLEASE, 0.15],
  ],
  [IndustrialPropertyType.LOGISTICS_CENTER]: [
    [IndustrialLeaseType.WAREHOUSE_LEASE, 1.0],
  ],
  [IndustrialPropertyType.OFFICE_IN_PARK]: [
    [IndustrialLeaseType.FACTORY_LEASE, 1.0],
  ],
  [IndustrialPropertyType.DATA_CENTER]: [
    [IndustrialLeaseType.FACTORY_LEASE, 0.6],
    [IndustrialLeaseType.SUBLEASE, 0.4],
  ],
};

const STATUS_PROBS: ReadonlyArray<readonly [IndustrialListingStatus, number]> = [
  [IndustrialListingStatus.ACTIVE, 0.8],
  [IndustrialListingStatus.DRAFT, 0.1],
  [IndustrialListingStatus.RESERVED, 0.05],
  [IndustrialListingStatus.LEASED, 0.05],
];

const SELLERS = ['seed-seller-001', 'seed-seller-002'];
const AGENTS = [
  'seed-agentprofile-001',
  'seed-agentprofile-002',
  'seed-agentprofile-003',
  null,
];

// ---------------------------------------------------------------------------
// Title / description templates (Vietnamese). Deliberately generic, no PII.
// ---------------------------------------------------------------------------
function titleFor(t: IndustrialPropertyType, areaM2: number, parkId: string): string {
  const kcnShort = parkId.replace('seed-kcn-', 'KCN#');
  const m2 = Math.round(areaM2);
  switch (t) {
    case IndustrialPropertyType.INDUSTRIAL_LAND:
      return `Đất công nghiệp ${m2.toLocaleString('vi-VN')}m² tại ${kcnShort}`;
    case IndustrialPropertyType.READY_BUILT_FACTORY:
      return `Nhà xưởng xây sẵn ${m2.toLocaleString('vi-VN')}m² tại ${kcnShort}`;
    case IndustrialPropertyType.READY_BUILT_WAREHOUSE:
      return `Kho xưởng xây sẵn ${m2.toLocaleString('vi-VN')}m² tại ${kcnShort}`;
    case IndustrialPropertyType.LOGISTICS_CENTER:
      return `Trung tâm logistics ${m2.toLocaleString('vi-VN')}m² tại ${kcnShort}`;
    case IndustrialPropertyType.OFFICE_IN_PARK:
      return `Văn phòng trong KCN ${m2.toLocaleString('vi-VN')}m² tại ${kcnShort}`;
    case IndustrialPropertyType.DATA_CENTER:
      return `Data center ${m2.toLocaleString('vi-VN')}m² tại ${kcnShort}`;
  }
}

function descriptionFor(
  t: IndustrialPropertyType,
  areaM2: number,
  region: VietnamRegion,
): string {
  const regionLabel =
    region === VietnamRegion.NORTH ? 'miền Bắc' :
    region === VietnamRegion.SOUTH ? 'miền Nam' : 'miền Trung';
  const m2 = Math.round(areaM2).toLocaleString('vi-VN');
  switch (t) {
    case IndustrialPropertyType.INDUSTRIAL_LAND:
      return `Lô đất công nghiệp ${m2}m² tại ${regionLabel}. Hạ tầng KCN hoàn chỉnh, pháp lý rõ ràng, phù hợp xây dựng nhà máy.`;
    case IndustrialPropertyType.READY_BUILT_FACTORY:
      return `Nhà xưởng xây sẵn ${m2}m² tại ${regionLabel}. Kết cấu thép tiền chế, nền bê tông chịu tải, hệ thống PCCC, điện 3 pha, nước và xử lý nước thải đầy đủ.`;
    case IndustrialPropertyType.READY_BUILT_WAREHOUSE:
      return `Kho xưởng xây sẵn ${m2}m² tại ${regionLabel}. Nền epoxy chịu tải, hệ thống dock container, PCCC tự động, phù hợp logistics và phân phối.`;
    case IndustrialPropertyType.LOGISTICS_CENTER:
      return `Trung tâm logistics ${m2}m² tại ${regionLabel}. Nhiều dock container, bãi xe tải, phù hợp kho ngoại quan và trung chuyển.`;
    case IndustrialPropertyType.OFFICE_IN_PARK:
      return `Văn phòng trong KCN ${m2}m² tại ${regionLabel}. Điều hòa trung tâm, thang máy, bãi đỗ xe, phù hợp văn phòng điều hành nhà máy lân cận.`;
    case IndustrialPropertyType.DATA_CENTER:
      return `Data center ${m2}m² tại ${regionLabel}. Nguồn điện dự phòng kép, hệ thống làm mát N+1, kết nối cáp quang đa nhà mạng.`;
  }
}

// ---------------------------------------------------------------------------
// Row synthesis.
// ---------------------------------------------------------------------------
interface SynthListing {
  id: string;
  parkId: string;
  sellerId: string;
  agentId: string | null;
  propertyType: IndustrialPropertyType;
  leaseType: IndustrialLeaseType;
  status: IndustrialListingStatus;
  title: string;
  description: string;
  areaM2: number;
  ceilingHeightM: number | null;
  floorLoadTonM2: number | null;
  columnSpacingM: number | null;
  dockCount: number | null;
  craneCapacityTon: number | null;
  hasMezzanine: boolean;
  hasOfficeArea: boolean;
  officeAreaM2: number | null;
  priceUsdM2: number | null;
  pricingUnit: string | null;
  totalLeasePrice: number | null;
  managementFee: number | null;
  depositMonths: number | null;
  minLeaseYears: number | null;
  maxLeaseYears: number | null;
  availableFrom: Date | null;
  powerCapacityKva: number | null;
  waterSupplyM3Day: number | null;
}

function priceForRow(
  park: ParkRef,
  t: IndustrialPropertyType,
  leaseType: IndustrialLeaseType,
): { priceUsdM2: number; pricingUnit: string } {
  const b = BOUNDS[t];
  const noise = randRange(0.85, 1.15);
  const subleaseAdj = leaseType === IndustrialLeaseType.SUBLEASE ? 0.9 : 1.0;
  switch (b.pricingMode) {
    case 'land': {
      const base = park.landRentUsdM2Year ?? 80;
      return {
        priceUsdM2: roundTo(base * b.priceBias * noise * subleaseAdj, 2),
        pricingUnit: 'usd/m2/year',
      };
    }
    case 'rbf':
    case 'data_center': {
      const base = park.rbfRentUsdM2Month ?? 4.5;
      return {
        priceUsdM2: roundTo(base * b.priceBias * noise * subleaseAdj, 2),
        pricingUnit: 'usd/m2/month',
      };
    }
    case 'rbw': {
      const base = park.rbwRentUsdM2Month ?? 4.0;
      return {
        priceUsdM2: roundTo(base * b.priceBias * noise * subleaseAdj, 2),
        pricingUnit: 'usd/m2/month',
      };
    }
    case 'office': {
      // Office is priced off factory rent with +50% bias (per plan).
      const base = park.rbfRentUsdM2Month ?? 4.5;
      return {
        priceUsdM2: roundTo(base * b.priceBias * noise * subleaseAdj, 2),
        pricingUnit: 'usd/m2/month',
      };
    }
  }
}

function generateOne(
  index: number,
  region: VietnamRegion,
  t: IndustrialPropertyType,
): SynthListing {
  const regionParks = PARKS_BY_REGION[region];
  if (regionParks.length === 0) {
    throw new Error(`No parks in region ${region}`);
  }
  const park = regionParks[Math.floor(rand() * regionParks.length)]!;
  const b = BOUNDS[t];
  const leaseType = pickWeighted(LEASE_TYPE_PROBS[t]);
  const status = pickWeighted(STATUS_PROBS);

  const areaM2 = roundTo(randRange(b.areaM2[0], b.areaM2[1]), 0);
  const ceilingHeightM = b.ceilingHeightM
    ? roundTo(randRange(b.ceilingHeightM[0], b.ceilingHeightM[1]), 1)
    : null;
  const floorLoadTonM2 = b.floorLoadTonM2
    ? roundTo(randRange(b.floorLoadTonM2[0], b.floorLoadTonM2[1]), 1)
    : null;
  const columnSpacingM = b.columnSpacingM
    ? roundTo(randRange(b.columnSpacingM[0], b.columnSpacingM[1]), 0)
    : null;
  const dockCount = b.dockCount ? randInt(b.dockCount[0], b.dockCount[1]) : null;
  const hasCrane = b.craneChance > 0 && rand() < b.craneChance;
  const craneCapacityTon =
    hasCrane && b.craneCapacityTon
      ? roundTo(randRange(b.craneCapacityTon[0], b.craneCapacityTon[1]), 0)
      : null;
  const hasMezzanine = rand() < b.mezzanineChance;
  const hasOfficeArea = rand() < b.officeChance;
  const officeAreaM2 =
    hasOfficeArea && b.officeFraction[1] > 0
      ? roundTo(areaM2 * randRange(b.officeFraction[0], b.officeFraction[1]), 0)
      : null;
  const powerCapacityKva = b.powerCapacityKva
    ? roundTo(randRange(b.powerCapacityKva[0], b.powerCapacityKva[1]), 0)
    : null;
  const waterSupplyM3Day = b.waterSupplyM3Day
    ? roundTo(randRange(b.waterSupplyM3Day[0], b.waterSupplyM3Day[1]), 0)
    : null;

  // Price only populated when status is not DRAFT — DRAFT rows intentionally
  // left null to exercise "missing label" paths in the AVM pipeline.
  let priceUsdM2: number | null = null;
  let pricingUnit: string | null = null;
  let totalLeasePrice: number | null = null;
  if (status !== IndustrialListingStatus.DRAFT) {
    const p = priceForRow(park, t, leaseType);
    priceUsdM2 = p.priceUsdM2;
    pricingUnit = p.pricingUnit;
    totalLeasePrice = roundTo(priceUsdM2 * areaM2, 0);
  } else {
    // Even DRAFT rows get a pricing unit hint so the AVM can condition on it.
    const p = priceForRow(park, t, leaseType);
    pricingUnit = p.pricingUnit;
  }

  const minLeaseYears =
    t === IndustrialPropertyType.INDUSTRIAL_LAND ? randInt(20, 30) : randInt(1, 5);
  const maxLeaseYears =
    t === IndustrialPropertyType.INDUSTRIAL_LAND ? randInt(40, 50) : randInt(5, 20);

  const availableOffsetDays = randInt(0, 180);
  const availableFrom = new Date(Date.now() + availableOffsetDays * 24 * 60 * 60 * 1000);

  const id = `synth-ind-listing-${String(index + 1).padStart(4, '0')}`;
  const sellerId = SELLERS[Math.floor(rand() * SELLERS.length)]!;
  const agentId = AGENTS[Math.floor(rand() * AGENTS.length)] ?? null;

  return {
    id,
    parkId: park.id,
    sellerId,
    agentId,
    propertyType: t,
    leaseType,
    status,
    title: titleFor(t, areaM2, park.id),
    description: descriptionFor(t, areaM2, park.region),
    areaM2,
    ceilingHeightM,
    floorLoadTonM2,
    columnSpacingM,
    dockCount,
    craneCapacityTon,
    hasMezzanine,
    hasOfficeArea,
    officeAreaM2,
    priceUsdM2,
    pricingUnit,
    totalLeasePrice,
    managementFee: park.managementFeeUsd,
    depositMonths: randInt(2, 6),
    minLeaseYears,
    maxLeaseYears,
    availableFrom,
    powerCapacityKva,
    waterSupplyM3Day,
  };
}

function generateAll(): SynthListing[] {
  const rows: SynthListing[] = [];
  let i = 0;
  for (const bucket of STRATIFICATION) {
    for (let j = 0; j < bucket.count; j++) {
      rows.push(generateOne(i, bucket.region, bucket.propertyType));
      i++;
    }
  }
  return rows;
}

// ---------------------------------------------------------------------------
// DB write path.
// ---------------------------------------------------------------------------
export async function seedIndustrialListingsSynth(): Promise<void> {
  const pool = new pg.Pool({ connectionString: process.env['DATABASE_URL'] });
  const adapter = new PrismaPg(pool);
  const prisma = new PrismaClient({ adapter });

  console.log(`🏭 Seeding synthetic industrial listings (seed=${SEED}, target=${TOTAL_EXPECTED})...`);

  const rows = generateAll();
  if (rows.length !== TOTAL_EXPECTED) {
    throw new Error(
      `Generator invariant broken: produced ${rows.length} rows, expected ${TOTAL_EXPECTED}`,
    );
  }

  const now = new Date();
  let written = 0;
  try {
    for (const l of rows) {
      const isPublished =
        l.status === IndustrialListingStatus.ACTIVE ||
        l.status === IndustrialListingStatus.RESERVED;
      await prisma.industrialListing.upsert({
        where: { id: l.id },
        update: {
          status: l.status,
          priceUsdM2: l.priceUsdM2,
          totalLeasePrice: l.totalLeasePrice,
          pricingUnit: l.pricingUnit,
        },
        create: {
          id: l.id,
          parkId: l.parkId,
          sellerId: l.sellerId,
          agentId: l.agentId,
          propertyType: l.propertyType,
          leaseType: l.leaseType,
          status: l.status,
          title: l.title,
          description: l.description,
          areaM2: l.areaM2,
          ceilingHeightM: l.ceilingHeightM,
          floorLoadTonM2: l.floorLoadTonM2,
          columnSpacingM: l.columnSpacingM,
          dockCount: l.dockCount,
          craneCapacityTon: l.craneCapacityTon,
          hasMezzanine: l.hasMezzanine,
          hasOfficeArea: l.hasOfficeArea,
          officeAreaM2: l.officeAreaM2,
          priceUsdM2: l.priceUsdM2,
          pricingUnit: l.pricingUnit,
          totalLeasePrice: l.totalLeasePrice,
          managementFee: l.managementFee,
          depositMonths: l.depositMonths,
          minLeaseYears: l.minLeaseYears,
          maxLeaseYears: l.maxLeaseYears,
          availableFrom: l.availableFrom,
          powerCapacityKva: l.powerCapacityKva,
          waterSupplyM3Day: l.waterSupplyM3Day,
          viewCount: Math.floor(rand() * 200) + 5,
          inquiryCount: Math.floor(rand() * 15),
          publishedAt: isPublished ? now : null,
        },
      });
      written++;
    }
    console.log(`🏭 Seeded ${written} synthetic industrial listings.`);
  } finally {
    await prisma.$disconnect();
    await pool.end();
  }
}

// Standalone entry point.
async function main(): Promise<void> {
  try {
    await seedIndustrialListingsSynth();
  } catch (err) {
    console.error('Synthetic seed error:', err);
    process.exit(1);
  }
}

if (require.main === module) {
  void main();
}
