/**
 * Multideal DESTRUCTIVE demo-reset script.
 * Run: CONFIRM_DB_RESET=1 pnpm db:reset-demo
 *
 * GUARD: refuses to run unless:
 *   1. CONFIRM_DB_RESET === '1' (prompts interactive confirmation) or 'force' (no prompt)
 *   2. DATABASE_URL does NOT include the main-branch compute endpoint
 *
 * Requires env vars: DATABASE_URL, PII_KEY
 */

import * as readline from 'readline';
import { scriptOutput } from '../lib/script-output.js';
import { readSeedDemoGuardEnv, readSeedDemoRuntimeEnv } from './seed-cli-env.js';
import * as seedWrites from './queries/seed-demo-writes.js';

// ─── Guard preamble ───────────────────────────────────────────────────────────

const MAIN_BRANCH_ENDPOINT = 'ep-summer-mode-aeq43rh1';

async function runGuard(): Promise<void> {
  const { databaseUrl: DATABASE_URL, confirm: CONFIRM } = readSeedDemoGuardEnv();

  // Guard 1: CONFIRM_DB_RESET must be set
  if (CONFIRM !== '1' && CONFIRM !== 'force') {
    console.error('DESTRUCTIVE_RESET_BLOCKED: set CONFIRM_DB_RESET=1 to run demo reset');
    process.exit(2);
  }

  // Guard 2: must not point at main branch
  if (DATABASE_URL.includes(MAIN_BRANCH_ENDPOINT)) {
    console.error('DESTRUCTIVE_RESET_BLOCKED: DATABASE_URL points to main branch');
    process.exit(2);
  }

  // Guard 3: interactive confirmation (skipped when CONFIRM === 'force')
  if (CONFIRM === '1') {
    const answer = await new Promise<string>((resolve) => {
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
      });
      rl.question('Type DELETE ALL DATA to confirm destructive reset: ', (ans) => {
        rl.close();
        resolve(ans);
      });
    });
    if (answer.trim() !== 'DELETE ALL DATA') {
      console.error('ABORTED: confirmation phrase mismatch');
      process.exit(2);
    }
  }
}

// ─── Imports (after guard passes) ────────────────────────────────────────────

import { drizzle } from 'drizzle-orm/neon-serverless';
import { Pool, neonConfig } from '@neondatabase/serverless';
import ws from 'ws';
import { sql } from 'drizzle-orm';
import { composedSchema } from './schema-composed.js';
import { encrypt } from './crypto.js';
import { setUserPhone, setUserEmail } from './pii-write.js';
import { refreshDealCache } from '../domain/variants/cache.js';
import { seedSupportSystemConfig } from './seeds/support-system-config.js';
import { seedSupportKb } from './seeds/support-kb.js';

// ─── Seeded PRNG (mulberry32) ─────────────────────────────────────────────────

function mulberry32(seed: number) {
  return function () {
    let t = (seed += 0x6d2b79f5);
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

const rng = mulberry32(0xdeadbeef);

function pick<T>(arr: T[]): T {
  return arr[Math.floor(rng() * arr.length)]!;
}

function randInt(min: number, max: number) {
  return min + Math.floor(rng() * (max - min + 1));
}

function randFloat(min: number, max: number, dp = 2) {
  return parseFloat((min + rng() * (max - min)).toFixed(dp));
}

function daysAgo(n: number) {
  return new Date(Date.now() - n * 86_400_000);
}
function daysFromNow(n: number) {
  return new Date(Date.now() + n * 86_400_000);
}

// ─── Fixture data ─────────────────────────────────────────────────────────────

const VENDOR_FIXTURES = [
  {
    business: 'מאפיית לחם שלום',
    display: 'לחם שלום',
    categories: ['אוכל', 'מאפייה'],
  },
  {
    business: 'סושי בר הכרם',
    display: 'סושי הכרם',
    categories: ['אוכל', 'יפני'],
  },
  { business: 'ספא נפש ורוח', display: 'ספא נפש', categories: ['יופי', 'ספא'] },
  {
    business: 'פיצרייה דה לוקה',
    display: 'דה לוקה',
    categories: ['אוכל', 'פיצה'],
  },
  {
    business: 'חנות הספרים העברי',
    display: 'הספרים העברי',
    categories: ['ספרים', 'תרבות'],
  },
  {
    business: 'קפה רוטשילד 22',
    display: 'רוטשילד 22',
    categories: ['קפה', 'אוכל'],
  },
  {
    business: 'סטודיו יוגה ותנועה',
    display: 'יוגה ותנועה',
    categories: ['כושר', 'יוגה'],
  },
  {
    business: 'עוגות שי לכם',
    display: 'עוגות שי',
    categories: ['אוכל', 'קינוחים'],
  },
  {
    business: 'ברבר שופ תל אביב',
    display: 'ברבר שופ',
    categories: ['יופי', 'ספרות'],
  },
  { business: 'חוות הוינריה', display: 'הוינריה', categories: ['אוכל', 'יין'] },
  {
    business: 'גלידריית ממתק',
    display: 'ממתק',
    categories: ['אוכל', 'קינוחים'],
  },
  {
    business: 'מסעדת אבו חסן',
    display: 'אבו חסן',
    categories: ['אוכל', 'ערבי'],
  },
  {
    business: 'פלאוור - סידור פרחים',
    display: 'פלאוור',
    categories: ['פרחים', 'מתנות'],
  },
  {
    business: 'חדר בריחה אורבן',
    display: 'חדר בריחה',
    categories: ['בידור', 'חוויות'],
  },
  {
    business: 'מכבסה אקספרס',
    display: 'אקספרס כביסה',
    categories: ['שירותים'],
  },
  {
    business: 'פיט קיטשן תל אביב',
    display: 'פיט קיטשן',
    categories: ['אוכל', 'בריאות'],
  },
  {
    business: 'מסג׳ תאילנדי ניתאי',
    display: 'מסג׳ ניתאי',
    categories: ['יופי', 'מסג׳'],
  },
  {
    business: 'חומוס בלדי שוק הכרמל',
    display: 'חומוס בלדי',
    categories: ['אוכל', 'מזרח תיכוני'],
  },
  {
    business: 'צילום ממריא - סטודיו',
    display: 'סטודיו ממריא',
    categories: ['צילום', 'חוויות'],
  },
  {
    business: 'כרטיסי מופע ישראל',
    display: 'מופע ישראל',
    categories: ['תרבות', 'כרטיסים'],
  },
];

const DEAL_TITLES = [
  'לחם שיפון שלם עם זיתים',
  'מגש סושי 16 יח׳ מעורב',
  'עיסוי שוודי שעה',
  'פיצה מרגריטה אישית + שתייה',
  'ספר לפי בחירה מהמדף',
  'קפה שחור + קרואסון',
  'שיעור יוגה ראשון',
  'עוגת מוס שוקולד',
  'תספורת + עיצוב',
  'ניחוש יין עם גוֹון',
  'גביע גלידה כפול',
  'פלאפל + חומוס + פיתה',
  'זר פרחים עונתי',
  'כניסה לחדר בריחה לזוג',
  'כביסה + קיפול 5 ק״ג',
  'קערת אקאי + גרנולה',
  'מסג׳ גב + כתפיים 45 דק',
  'מנת חומוס + פיתה טרייה',
  'שיעור צילום נוף',
  'כרטיס לסטנדאפ קומדי',
  'קרואסון שקדים + לאטה',
  'מגש קינוחים לשניים',
  'פיצה ארבע גבינות',
  'ספר ילדים + גלויה',
  'שייק ירוק + חטיף אנרגיה',
  'עיסוי רגליים תאילנדי 30 דק',
  'לחמניות בריוש 6 יח׳',
  'מנת שקשוקה ולחם',
  'חבילת צילום משפחה 30 דק',
  'כרטיס לתערוכה',
];

const DEAL_DESCRIPTIONS = [
  'מוצר טרי ואיכותי מדי יום, מוכן עם אהבה ומרכיבים מהשוק.',
  'חוויה אמיתית עם חומרי גלם טריים ממקורות מקומיים.',
  'שירות מקצועי ואישי - נשמח לראותכם.',
  'ללא צבעי מאכל מלאכותיים. מוכן בהזמנה.',
  'גרסה קלאסית בטעם אמיתי, בדיוק כמו תמיד.',
];

// Tel Aviv coordinates roughly
const TEL_AVIV_LATS = [32.065, 32.071, 32.078, 32.082, 32.062, 32.09];
const TEL_AVIV_LNGS = [34.773, 34.781, 34.789, 34.77, 34.763, 34.794];

// ─── Main ──────────────────────────────────────────────────────────────────────

async function main() {
  await runGuard();

  const { DATABASE_URL, PII_KEY, PASSWORD_PEPPER_V1 } = readSeedDemoRuntimeEnv();
  if (!DATABASE_URL) throw new Error('DATABASE_URL env var required');
  if (!PII_KEY) throw new Error('PII_KEY env var required');
  if (!PASSWORD_PEPPER_V1) throw new Error('PASSWORD_PEPPER_V1 env var required');

  neonConfig.webSocketConstructor = ws;
  const pool = new Pool({ connectionString: DATABASE_URL });
  const schema = composedSchema;
  const db = drizzle(pool, { schema });

  scriptOutput('Demo reset: seeding Multideal database…');

  // ── Enable pgcrypto if not present ──────────────────────────────────────────
  await db.execute(sql`CREATE EXTENSION IF NOT EXISTS pgcrypto`);

  // ── Wipe existing seed data ──────────────────────────────────────────────────
  const tables = [
    'outbox',
    'review_removal_queue',
    'rate_limit_buckets',
    'report_tickets',
    'image_uploads',
    'user_gallery_images',
    'vendor_gallery_images',
    'personal_deal_requests',
    'push_subscriptions',
    'magic_links',
    'sessions',
    'purchase_messages',
    'admin_actions',
    'club_memberships',
    'reviews',
    'purchases',
    'deal_images',
    'deals',
    'vendor_addresses',
    'business_hours',
    'vendors',
    'payment_methods',
    'addresses',
    'users',
  ];
  await db.execute(sql.raw(`TRUNCATE ${tables.join(', ')} CASCADE`));

  // ── 1. Create admin user ─────────────────────────────────────────────────────
  scriptOutput('Creating admin user…');
  {
    const { hashPassword } = await import('../auth/credentials.js');
    const adminEmail = 'admin@example.dev';
    const adminPassword = 'OQPDnd9QCK6K-2DB@Multideal1';
    const adminPasswordHash = await hashPassword(adminPassword, PASSWORD_PEPPER_V1);
    const adminEmailPatch = await setUserEmail(adminEmail, PII_KEY);
    await seedWrites.insertSeedDemoAdminUser(db, {
      ...adminEmailPatch,
      passwordHash: adminPasswordHash,
      isAdmin: true,
      avatarType: 'ICON',
      avatarValue: 'icon_1',
      purchaseCount: 0,
      accountState: 'ACTIVE',
    });
  }

  // ── 2. Create 100 users ──────────────────────────────────────────────────────
  scriptOutput('Creating users…');
  const userIds: string[] = [];
  for (let i = 0; i < 100; i++) {
    const rawPhone = `+9725${String(10000000 + i).padStart(8, '0')}`;
    const rawEmail = `user${i + 1}@multideal-seed.test`;
    const phonePatch = await setUserPhone(rawPhone, PII_KEY);
    const emailPatch = await setUserEmail(rawEmail, PII_KEY);

    const user = await seedWrites.insertSeedDemoUser(db, {
      ...phonePatch,
      ...emailPatch,
      avatarType: rng() > 0.5 ? 'ICON' : 'GRAVATAR',
      avatarValue: `icon_${randInt(1, 12)}`,
      purchaseCount: 0,
      accountState: 'ACTIVE',
      isAdmin: false,
    });

    userIds.push(user!.id);

    // 1-3 addresses per user (max 3 per FDS)
    const addrCount = randInt(1, 3);
    for (let a = 0; a < addrCount; a++) {
      const lat = pick(TEL_AVIV_LATS);
      const lng = pick(TEL_AVIV_LNGS);
      await seedWrites.insertSeedDemoAddress(db, {
        userId: user!.id,
        label: a === 0 ? 'בית' : a === 1 ? 'עבודה' : 'אחר',
        fullAddress: encrypt(
          `רחוב דיזנגוף ${randInt(1, 200)}, תל אביב`,
          PII_KEY,
        ) as unknown as string,
        lat: lat.toFixed(7),
        lng: lng.toFixed(7),
      });
    }

    // 1 payment method per user
    await seedWrites.insertSeedDemoPaymentMethod(db, {
      userId: user!.id,
      last4: String(1000 + randInt(0, 8999)),
      brand: pick(['VISA', 'MASTERCARD', 'AMEX', 'BIT', 'PAYPAL', 'GOOGLE_PAY'] as const),
      token: encrypt(`tok_seed_${i}`, PII_KEY) as unknown as string,
      isDefault: true,
    });
  }

  // ── 3. Create 20 vendors ──────────────────────────────────────────────────────
  scriptOutput('Creating vendors…');
  const vendorIds: string[] = [];
  for (let i = 0; i < 20; i++) {
    const fix = VENDOR_FIXTURES[i]!;
    const ownerIdx = i * 5; // users 0,5,10… own vendors (some users own vendors)
    const rawPhone = `+9722${String(50000000 + i).padStart(8, '0')}`;
    const rawEmail = `vendor${i + 1}@multideal-seed.test`;

    const vendor = await seedWrites.insertSeedDemoVendor(db, {
      ownerUserId: userIds[ownerIdx % userIds.length]!,
      businessName: fix.business,
      displayName: fix.display,
      description: pick(DEAL_DESCRIPTIONS),
      phone: encrypt(rawPhone, PII_KEY) as unknown as string,
      email: encrypt(rawEmail, PII_KEY) as unknown as string,
      tier: i < 4 ? 'VETERAN' : 'NEW',
      accountState: i < 2 ? 'ACTIVE' : i === 19 ? 'FROZEN' : 'ACTIVE',
      totalSales: randInt(10, 300),
      totalRevenue: randFloat(500, 30000).toFixed(2),
      reviewsScore: randFloat(3.5, 5.0, 2).toFixed(2),
      reviewsCount: randInt(5, 80),
      // Stripe Connect: seed vendors start without a connected account
      stripeAccountId: null,
      stripeChargesEnabled: false,
      stripePayoutsEnabled: false,
    });

    vendorIds.push(vendor!.id);

    // Business hours
    const lat = pick(TEL_AVIV_LATS);
    const lng = pick(TEL_AVIV_LNGS);
    await seedWrites.insertSeedDemoBusinessHours(db, {
      vendorId: vendor!.id,
      mondayOpen: '09:00',
      mondayClose: '22:00',
      mondayClosed: false,
      tuesdayOpen: '09:00',
      tuesdayClose: '22:00',
      tuesdayClosed: false,
      wednesdayOpen: '09:00',
      wednesdayClose: '22:00',
      wednesdayClosed: false,
      thursdayOpen: '09:00',
      thursdayClose: '22:00',
      thursdayClosed: false,
      fridayOpen: '09:00',
      fridayClose: '15:00',
      fridayClosed: false,
      saturdayClosed: true,
      sundayOpen: '10:00',
      sundayClose: '20:00',
      sundayClosed: false,
    });

    // Vendor address
    await seedWrites.insertSeedDemoVendorAddress(db, {
      vendorId: vendor!.id,
      label: 'ראשי',
      fullAddress: `שדרות רוטשילד ${randInt(1, 100)}, תל אביב`,
      city: 'תל אביב',
      cityCode: 'tel-aviv',
      lat: lat.toFixed(7),
      lng: lng.toFixed(7),
      isPublic: true,
    });
  }

  // ── 4. Create 50 deals ────────────────────────────────────────────────────────
  scriptOutput('Creating deals…');
  const dealTypes = ['COUPON', 'GROUP'] as const;
  const dealStates = [
    'ACTIVE',
    'ACTIVE',
    'ACTIVE',
    'ACTIVE',
    'ACTIVE',
    'ACTIVE',
    'ACTIVE',
    'PENDING_APPROVAL',
    'PENDING_APPROVAL',
    'EXPIRED',
    'EXPIRED',
    'EXPIRED',
    'SOLD_OUT',
    'SOLD_OUT',
    'REJECTED',
    'PAUSED',
    'DRAFT',
  ] as const;

  const dealIds: string[] = [];
  const dealSkuByDealId = new Map<string, string>();
  for (let i = 0; i < 50; i++) {
    const vendorId = vendorIds[i % vendorIds.length]!;
    const dealType = dealTypes[i % dealTypes.length]!;
    const rawState = dealStates[i % dealStates.length]!;
    const title = DEAL_TITLES[i % DEAL_TITLES.length]!;
    const origPrice = randFloat(40, 300, 2);
    const discount = pick([50, 50, 50, 60, 60, 70, 75, 80]);
    const discountedPrice = parseFloat((origPrice * (1 - discount / 100)).toFixed(2));

    const isActive = rawState === 'ACTIVE';
    // Vary window: some active deals expire soon (whats-left), most have long window
    const windowStart = isActive ? daysAgo(1) : daysAgo(randInt(2, 10));
    const windowEnd = isActive
      ? i % 7 === 0
        ? daysFromNow(0) // expires today → whats-left
        : daysFromNow(randInt(7, 60))
      : daysAgo(randInt(0, 5));
    const quantityTotal = randInt(10, 60);
    // Some active deals have high sold% → hot-deals row
    const quantitySold =
      rawState === 'SOLD_OUT'
        ? quantityTotal
        : rawState === 'ACTIVE'
          ? i % 5 === 0
            ? Math.floor(quantityTotal * 0.8) // 80% sold → hot deal
            : randInt(1, Math.max(1, Math.floor(quantityTotal * 0.5)))
          : 0;

    const deal = await seedWrites.insertSeedDemoDeal(db, {
      vendorId,
      dealType,
      title,
      description: pick(DEAL_DESCRIPTIONS),
      categoryId: null,
      windowStart,
      windowEnd,
      pickupStart: '10:00',
      pickupEnd: '20:00',
      pickupAddress: `שדרות רוטשילד ${randInt(1, 100)}, תל אביב`,
      specialInstructions: rng() > 0.7 ? 'יש להציג קוד QR בכניסה' : null,
      dealState: rawState,
      rejectionReason: rawState === 'REJECTED' ? 'תמונה לא מתאימה' : null,
      rejectionDetail:
        rawState === 'REJECTED'
          ? 'התמונה שהועלתה אינה עומדת בתקנים. אנא העלו תמונה ברורה של המוצר.'
          : null,
      commissionRate: '0.100',
      approvedAt: ['ACTIVE', 'SOLD_OUT', 'EXPIRED', 'PAUSED'].includes(rawState)
        ? daysAgo(randInt(1, 5))
        : null,
      approvedBy: ['ACTIVE', 'SOLD_OUT', 'EXPIRED', 'PAUSED'].includes(rawState)
        ? rng() > 0.3
          ? 'HUMAN'
          : 'AI_AGENT'
        : null,
      soldOutAt: rawState === 'SOLD_OUT' ? daysAgo(1) : null,
      soldOutGoldExpiresAt: rawState === 'SOLD_OUT' ? daysAgo(0) : null,
    });

    dealIds.push(deal!.id);

    // Default SKU — carries per-unit price and stock data
    const sku = await seedWrites.insertSeedDemoDealSku(db, {
      dealId: deal!.id,
      optionIds: [],
      optionIdsHash: 'default',
      originalPrice: origPrice.toFixed(2),
      discountPercent: discount,
      discountedPrice: discountedPrice.toFixed(2),
      quantityTotal,
      quantitySold,
      isActive: true,
    });
    dealSkuByDealId.set(deal!.id, sku!.id);
    await refreshDealCache(db, deal!.id);

    // Deal image
    await seedWrites.insertSeedDemoDealImage(db, {
      dealId: deal!.id,
      url: `uploads/deals/seed-${i + 1}/card.png`,
      isPrimary: true,
      sortOrder: 0,
      approvalStatus: ['ACTIVE', 'SOLD_OUT', 'EXPIRED'].includes(rawState) ? 'APPROVED' : 'PENDING',
    });
  }

  // ── 5. Create 200 purchases ───────────────────────────────────────────────────
  scriptOutput('Creating purchases…');
  const purchaseIds: string[] = [];
  const purchaseDealIds: string[] = [];
  // Only use ACTIVE/SOLD_OUT/EXPIRED deals for purchases
  const purchasableDeals = dealIds.filter((_, i) => {
    const state = [
      'ACTIVE',
      'ACTIVE',
      'ACTIVE',
      'ACTIVE',
      'ACTIVE',
      'ACTIVE',
      'ACTIVE',
      'PENDING_APPROVAL',
      'PENDING_APPROVAL',
      'EXPIRED',
      'EXPIRED',
      'EXPIRED',
      'SOLD_OUT',
      'SOLD_OUT',
      'REJECTED',
      'PAUSED',
      'DRAFT',
    ][i % 17];
    return ['ACTIVE', 'SOLD_OUT', 'EXPIRED'].includes(state ?? '');
  });

  const redemptionStates = [
    'UNREDEEMED',
    'UNREDEEMED',
    'UNREDEEMED',
    'REDEEMED',
    'REDEEMED',
    'REDEEMED',
    'REDEEMED',
    'EXPIRED',
    'EXPIRED',
    'CANCELLED',
  ] as const;

  for (let i = 0; i < 200; i++) {
    const userId = userIds[i % userIds.length]!;
    const dealId = purchasableDeals[i % Math.max(purchasableDeals.length, 1)] ?? dealIds[0]!;
    // Find vendor for this deal from our mapping (vendorId stored at index i%20)
    const vendorId = vendorIds[i % vendorIds.length]!;
    const amount = randFloat(15, 200, 2);
    const redemptionStatus = redemptionStates[i % redemptionStates.length]!;
    const createdAt = daysAgo(randInt(0, 90));
    const expiresAt = new Date(createdAt.getTime() + 30 * 86_400_000);

    const amountAgorot = BigInt(Math.round(amount * 100));
    const variantId = dealSkuByDealId.get(dealId) ?? dealSkuByDealId.values().next().value!;

    const newOrder = await seedWrites.insertSeedDemoPurchaseOrder(db, {
      buyerUserId: userId,
      status: 'paid',
      currency: 'ILS',
      priceMode: 'inclusive',
      subtotal: amountAgorot,
      tax: BigInt(0),
      discount: BigInt(0),
      total: amountAgorot,
      chargeRef: `pi_seed_${100000 + i}`,
      idempotencyKey: `seed-purchase-${i}`,
      requestHash: `seed-hash-${i}`,
    });

    const newLine = await seedWrites.insertSeedDemoPurchaseLine(db, {
      orderId: newOrder!.id,
      variantId,
      kind: 'voucher',
      qty: 1,
      unitPrice: amountAgorot,
      lineTotal: amountAgorot,
      vendorId,
    });

    const voucherId = crypto.randomUUID();
    const voucherState = redemptionStatus === 'CANCELLED' ? 'CANCELLED' : redemptionStatus;
    const voucherRedeemedAt =
      redemptionStatus === 'REDEEMED'
        ? new Date(createdAt.getTime() + randInt(1, 20) * 86_400_000)
        : null;
    await seedWrites.insertSeedDemoVoucher(db, {
      id: voucherId,
      orderId: newOrder!.id,
      lineId: newLine!.id,
      unitIndex: 0,
      vendorId,
      state: voucherState,
      redeemedAt: voucherRedeemedAt,
      expiresAt,
    });
    await seedWrites.insertSeedDemoVoucherExtension(db, {
      voucherId,
      lineId: newLine!.id,
      qrTokenHash: `hash_seed_${i}_${userId.slice(0, 8)}`,
      reviewEligible: redemptionStatus === 'REDEEMED',
    });

    purchaseIds.push(newLine!.id);
    purchaseDealIds.push(dealId);
  }

  // ── 6. Create ~80 reviews ─────────────────────────────────────────────────────
  scriptOutput('Creating reviews…');
  const redeemedPurchaseIds = purchaseIds.filter((_, i) => {
    const rdSt = redemptionStates[i % redemptionStates.length];
    return rdSt === 'REDEEMED';
  });
  const redeemedPurchaseDealIds = purchaseDealIds.filter((_, i) => {
    const rdSt = redemptionStates[i % redemptionStates.length];
    return rdSt === 'REDEEMED';
  });

  const removalRequestedCount = { count: 0 };
  for (let i = 0; i < Math.min(80, redeemedPurchaseIds.length); i++) {
    const purchaseId = redeemedPurchaseIds[i]!;
    const dealId = redeemedPurchaseDealIds[i]!;
    const userId = userIds[i % userIds.length]!;
    const vendorId = vendorIds[i % vendorIds.length]!;
    const isTechnical = i >= 70; // last 10 are technical
    const rating = isTechnical ? null : pick([5, 5, 5, 4, 4, 4, 3, 2]);
    const withReply = i < 10;
    const withRemoval = !isTechnical && removalRequestedCount.count < 3 && i >= 60;

    await seedWrites.insertSeedDemoReview(db, {
      orderLineId: purchaseId,
      dealId,
      userId,
      vendorId,
      reviewType: isTechnical ? 'TECHNICAL' : 'STANDARD',
      rating,
      body: isTechnical
        ? `בעיה טכנית: לא הצלחתי לפדות את השובר. מס׳ הזמנה: ${purchaseId.slice(0, 8)}`
        : pick([
            'מצוין! מוצר מעולה, שירות מהיר ואדיב.',
            'ממש שווה את הכסף. נחזור בהחלט.',
            'חוויה נהדרת, המוצר היה טרי ואיכותי.',
            'טוב מאד, ממליץ בחום.',
            'בסדר, לא מאוד מרשים אבל לא מאכזב.',
            'מוצר סביר, ציפיתי ליותר על פי התיאור.',
          ]),
      vendorReply: withReply ? 'תודה רבה על הביקורת! שמחים שנהניתם ומצפים לראותכם שוב.' : null,
      vendorReplyAt: withReply ? new Date(Date.now() - randInt(1, 5) * 86_400_000) : null,
      removalRequested: withRemoval,
      removalReason: withRemoval ? 'ביקורת כוזבת, הלקוח לא ביקר אצלנו' : null,
      removalStatus: withRemoval ? 'PENDING' : 'NONE',
      isVisible: true,
    });

    if (withRemoval) removalRequestedCount.count++;
  }

  // ── 7. Club memberships (derived from purchases) ───────────────────────────
  scriptOutput('Creating club memberships…');
  const seenPairs = new Set<string>();
  for (let i = 0; i < purchaseIds.length; i++) {
    const userId = userIds[i % userIds.length]!;
    const vendorId = vendorIds[i % vendorIds.length]!;
    const key = `${userId}:${vendorId}`;
    if (seenPairs.has(key)) continue;
    seenPairs.add(key);
    const purchaseId = purchaseIds[i]!;
    await seedWrites.insertSeedDemoClubMembership(db, {
      userId,
      vendorId,
      joinedViaOrderLineId: purchaseId,
    });
  }

  // ── Support system config + KB ───────────────────────────────────────────────
  scriptOutput('Seeding support system config…');
  await seedSupportSystemConfig(db);

  scriptOutput('Seeding support KB articles…');
  await seedSupportKb(db);

  scriptOutput('Demo reset complete!');
  scriptOutput(`  Users:        100`);
  scriptOutput(`  Vendors:       20`);
  scriptOutput(`  Deals:         50`);
  scriptOutput(`  Purchases:    200`);
  scriptOutput(`  Reviews:      ~80`);
  scriptOutput(`  Memberships:  ${seenPairs.size}`);
  scriptOutput(`  KB articles:   20 (10 × 2 locales)`);

  process.exit(0);
}

main().catch((err) => {
  console.error('Demo reset failed:', err);
  process.exit(1);
});
