/**
 * Synthetic Stress-Test Data Seed
 * ================================
 * Populates the ${BRAND.companyName} ERP with realistic synthetic data
 * modeled after a mid-market B2B tech services company (~150 employees).
 * Covers ~12 months of history so every report in the Report Center renders
 * meaningful results.
 *
 * RULES:
 *  - Uses upserts wherever possible (idempotent, no duplicates)
 *  - Never deletes existing data
 *  - All IDs are deterministic (prefixed with "syn-") so re-runs are safe
 */
import 'dotenv/config';
import { PrismaClient } from '@prisma/client';
import { BRAND } from '@/lib/white-label/constants';

const prisma = new PrismaClient();

// ── helpers ────────────────────────────────────────────────────────────────
const DAY = 86_400_000;
const now = new Date('2026-03-13T12:00:00Z');
function daysAgo(n: number) { return new Date(now.getTime() - n * DAY); }
function daysFromNow(n: number) { return new Date(now.getTime() + n * DAY); }
function rand(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; }
function pick<T>(arr: T[]): T { return arr[Math.floor(Math.random() * arr.length)]; }
function money(min: number, max: number) { return Math.round((Math.random() * (max - min) + min) * 100) / 100; }
function synId(prefix: string, i: number) { return `syn-${prefix}-${String(i).padStart(4, '0')}`; }

// ── reference data ─────────────────────────────────────────────────────────
const FIRST_NAMES = ['James','Mary','Robert','Patricia','John','Jennifer','Michael','Linda','David','Barbara','William','Elizabeth','Richard','Susan','Joseph','Jessica','Thomas','Sarah','Christopher','Karen','Daniel','Lisa','Matthew','Nancy','Anthony','Betty','Mark','Margaret','Donald','Sandra','Steven','Ashley','Paul','Dorothy','Andrew','Kimberly','Joshua','Emily','Kenneth','Donna','Kevin','Michelle','Brian','Carol','George','Amanda','Timothy','Melissa','Ronald','Deborah','Jason','Stephanie','Edward','Rebecca','Ryan','Sharon','Jacob','Laura','Gary','Cynthia','Nicholas','Kathleen','Eric','Amy','Jonathan','Angela','Stephen','Shirley','Larry','Anna','Justin','Brenda','Scott','Pamela','Brandon','Emma','Benjamin','Nicole','Samuel','Helen','Gregory','Samantha','Alexander','Katherine','Frank','Christine','Patrick','Debra','Raymond','Rachel','Jack','Carolyn','Dennis','Janet'];
const LAST_NAMES = ['Smith','Johnson','Williams','Brown','Jones','Garcia','Miller','Davis','Rodriguez','Martinez','Hernandez','Lopez','Gonzalez','Wilson','Anderson','Thomas','Taylor','Moore','Jackson','Martin','Lee','Perez','Thompson','White','Harris','Sanchez','Clark','Ramirez','Lewis','Robinson','Walker','Young','Allen','King','Wright','Scott','Torres','Nguyen','Hill','Flores','Green','Adams','Nelson','Baker','Hall','Rivera','Campbell','Mitchell','Carter','Roberts','Gomez','Phillips','Evans','Turner','Diaz','Parker','Cruz','Edwards','Collins','Reyes','Stewart','Morris','Morales','Murphy','Cook','Rogers','Gutierrez','Ortiz','Morgan','Cooper','Peterson','Bailey','Reed','Kelly','Howard','Ramos','Kim','Cox','Ward','Richardson','Watson','Brooks','Chavez','Wood','James','Bennett','Gray','Mendoza','Ruiz','Hughes','Price','Alvarez','Castillo','Sanders','Patel','Myers','Long','Ross','Foster','Jimenez'];
const INDUSTRIES = ['Technology','Healthcare','Financial Services','Manufacturing','Retail','Education','Energy','Telecommunications','Real Estate','Professional Services','Media & Entertainment','Logistics','Government','Aerospace','Automotive'];
const LEAD_SOURCES = ['website','referral','cold-call','conference','linkedin','partner','advertisement','webinar','content','inbound'];
const PRODUCT_CATEGORIES = ['Software','Hardware','Services','Support','Training','Consulting'];
const DEPARTMENTS_DEF = [
  { code: 'ENG', name: 'Engineering', headCount: 35 },
  { code: 'OPS', name: 'Operations', headCount: 15 },
  { code: 'SLS', name: 'Sales', headCount: 20 },
  { code: 'MKT', name: 'Marketing', headCount: 12 },
  { code: 'FIN', name: 'Finance', headCount: 10 },
  { code: 'HR', name: 'Human Resources', headCount: 8 },
  { code: 'CS', name: 'Customer Success', headCount: 15 },
  { code: 'EXEC', name: 'Executive', headCount: 5 },
];
const TITLES_BY_DEPT: Record<string, string[]> = {
  ENG: ['Software Engineer','Senior Engineer','Staff Engineer','Engineering Manager','DevOps Engineer','QA Engineer','Frontend Developer','Backend Developer','Platform Engineer','Tech Lead'],
  OPS: ['Operations Analyst','Operations Manager','Process Specialist','IT Support','Systems Administrator','Infrastructure Engineer'],
  SLS: ['Account Executive','Sales Manager','SDR','Enterprise AE','Regional Sales Director','Sales Operations Analyst','Business Development Rep'],
  MKT: ['Marketing Manager','Content Strategist','Growth Lead','Brand Designer','Marketing Analyst','Demand Gen Manager'],
  FIN: ['Financial Analyst','Controller','Staff Accountant','AR Specialist','AP Specialist','FP&A Analyst'],
  HR: ['HR Generalist','Recruiter','People Ops Manager','HR Business Partner','Benefits Coordinator'],
  CS: ['Customer Success Manager','Support Engineer','Implementation Specialist','Solutions Architect','Technical Account Manager'],
  EXEC: ['CEO','CTO','CFO','COO','VP Engineering'],
};
const ACCOUNT_CHART = [
  { code: 'SYN-1000', name: 'Operating Cash', type: 'asset', category: 'cash' },
  { code: 'SYN-1010', name: 'Payroll Cash', type: 'asset', category: 'cash' },
  { code: 'SYN-1100', name: 'Accounts Receivable', type: 'asset', category: 'receivable' },
  { code: 'SYN-1200', name: 'Prepaid Expenses', type: 'asset', category: 'prepaid' },
  { code: 'SYN-1300', name: 'Inventory Asset', type: 'asset', category: 'inventory' },
  { code: 'SYN-2000', name: 'Accounts Payable', type: 'liability', category: 'payable' },
  { code: 'SYN-2100', name: 'Accrued Payroll', type: 'liability', category: 'accrued' },
  { code: 'SYN-2200', name: 'Deferred Revenue', type: 'liability', category: 'deferred' },
  { code: 'SYN-3000', name: 'Retained Earnings', type: 'equity', category: 'equity' },
  { code: 'SYN-4000', name: 'Service Revenue', type: 'revenue', category: 'revenue' },
  { code: 'SYN-4100', name: 'Product Revenue', type: 'revenue', category: 'revenue' },
  { code: 'SYN-4200', name: 'Subscription Revenue', type: 'revenue', category: 'revenue' },
  { code: 'SYN-5000', name: 'Salaries & Wages', type: 'expense', category: 'expense' },
  { code: 'SYN-5100', name: 'Rent & Facilities', type: 'expense', category: 'expense' },
  { code: 'SYN-5200', name: 'Software & SaaS', type: 'expense', category: 'expense' },
  { code: 'SYN-5300', name: 'Marketing Spend', type: 'expense', category: 'expense' },
  { code: 'SYN-5400', name: 'Travel & Entertainment', type: 'expense', category: 'expense' },
  { code: 'SYN-5500', name: 'Professional Fees', type: 'expense', category: 'expense' },
  { code: 'SYN-5600', name: 'Cost of Goods Sold', type: 'expense', category: 'cogs' },
];

const VENDOR_NAMES = [
  'CloudStack Technologies','Nexus Software Partners','Pacific IT Supplies','Digital Ocean Solutions',
  'Apex Office Equipment','Metro Telecom Services','Summit Data Centers','Pinnacle Security Systems',
  'Horizon Marketing Agency','Sterling Legal Group','Elite Staffing Solutions','Green Energy Providers',
  'Quantum Networking Inc','ProFleet Logistics','Atlas Insurance Group'
];

const COMPANY_NAMES = [
  'Meridian Health Systems','Cascade Financial Group','Redwood Manufacturing','BlueWave Analytics',
  'Sterling Automotive','Nova Pharmaceuticals','Evergreen Education Corp','Titan Aerospace',
  'Harbor Logistics Inc','Summit Retail Group','Vertex Technologies','Ironclad Security Solutions',
  'Polaris Energy Corp','Stratos Telecommunications','Quantum Computing Labs','Pacific Real Estate Holdings',
  'Vanguard Professional Services','Nexus Media Group','Alpine Construction Co','Sapphire Healthcare',
  'Orion Defense Systems','Catalyst Innovation Hub','Phoenix Insurance Corp','Trident Maritime LLC','Zenith Capital Partners'
];

// ── main ───────────────────────────────────────────────────────────────────
async function main() {
  console.log('\n🧪 ═══════════════════════════════════════════════════');
  console.log('   SYNTHETIC STRESS-TEST DATA SEED');
  console.log('   Mid-market B2B tech services · ~12 months history');
  console.log('═══════════════════════════════════════════════════\n');

  // ╔═══════════════════════════════╗
  // ║  1. CHART OF ACCOUNTS        ║
  // ╚═══════════════════════════════╝
  console.log('📒 Chart of Accounts...');
  const accountMap: Record<string, string> = {};
  for (const acct of ACCOUNT_CHART) {
    const r = await prisma.account.upsert({
      where: { code: acct.code },
      update: { name: acct.name, type: acct.type, category: acct.category },
      create: { code: acct.code, name: acct.name, type: acct.type, category: acct.category, balance: money(10000, 500000) },
    });
    accountMap[acct.code] = r.id;
  }
  console.log(`  ✓ ${ACCOUNT_CHART.length} accounts`);

  // ╔═══════════════════════════════╗
  // ║  2. DEPARTMENTS               ║
  // ╚═══════════════════════════════╝
  console.log('🏢 Departments...');
  const deptMap: Record<string, string> = {};
  for (const d of DEPARTMENTS_DEF) {
    const r = await prisma.department.upsert({
      where: { code: d.code },
      update: { name: d.name, headCount: d.headCount },
      create: { name: d.name, code: d.code, description: `${d.name} department`, headCount: d.headCount },
    });
    deptMap[d.code] = r.id;
  }
  console.log(`  ✓ ${DEPARTMENTS_DEF.length} departments`);

  // ╔═══════════════════════════════╗
  // ║  3. EMPLOYEES (~50)           ║
  // ╚═══════════════════════════════╝
  console.log('👥 Employees...');
  const employeeIds: string[] = [];
  const deptCodes = DEPARTMENTS_DEF.map(d => d.code);
  let empIdx = 0;
  for (const deptCode of deptCodes) {
    const titles = TITLES_BY_DEPT[deptCode] || ['Specialist'];
    const count = deptCode === 'ENG' ? 12 : deptCode === 'SLS' ? 8 : deptCode === 'CS' ? 6 : deptCode === 'MKT' ? 5 : deptCode === 'OPS' ? 5 : deptCode === 'FIN' ? 5 : deptCode === 'HR' ? 4 : 3;
    for (let i = 0; i < count; i++) {
      empIdx++;
      const fn = FIRST_NAMES[empIdx % FIRST_NAMES.length];
      const ln = LAST_NAMES[empIdx % LAST_NAMES.length];
      const eid = synId('emp', empIdx);
      const empNumber = `EMP${String(100 + empIdx).padStart(3, '0')}`;
      const salary = deptCode === 'EXEC' ? rand(180000, 280000) : deptCode === 'ENG' ? rand(95000, 185000) : rand(55000, 130000);
      const r = await prisma.employee.upsert({
        where: { employeeId: empNumber },
        update: {},
        create: {
          id: eid,
          employeeId: empNumber,
          firstName: fn,
          lastName: ln,
          email: `${fn.toLowerCase()}.${ln.toLowerCase()}@ghostfoundry.ai`,
          departmentId: deptMap[deptCode],
          title: titles[i % titles.length],
          employmentType: Math.random() < 0.85 ? 'full-time' : 'contractor',
          salary,
          startDate: daysAgo(rand(60, 730)),
          status: Math.random() < 0.95 ? 'active' : 'inactive',
        },
      });
      employeeIds.push(r.id);
    }
  }
  console.log(`  ✓ ${employeeIds.length} employees`);

  // ╔═══════════════════════════════╗
  // ║  4. HR: BANK, LEAVE, TIME    ║
  // ╚═══════════════════════════════╝
  console.log('🏦 HR records...');
  // Bank accounts (one per employee)
  for (let i = 0; i < employeeIds.length; i++) {
    const bankId = synId('bank', i + 1);
    await prisma.hRBankAccount.upsert({
      where: { id: bankId },
      update: {},
      create: {
        id: bankId,
        employeeId: employeeIds[i],
        accountName: `${FIRST_NAMES[(i + 1) % FIRST_NAMES.length]} ${LAST_NAMES[(i + 1) % LAST_NAMES.length]}`,
        bankName: pick(['Chase','Wells Fargo','Bank of America','Citibank','US Bank','PNC']),
        accountNumber: `****${rand(1000, 9999)}`,
        routingNumber: `${rand(100000000, 999999999)}`,
        accountType: Math.random() < 0.85 ? 'checking' : 'savings',
        isPrimary: true,
        isActive: true,
      },
    });
  }
  // Leave requests (~80)
  const leaveTypes = ['vacation','sick','personal','bereavement','parental'];
  const leaveStatuses = ['pending','approved','rejected','cancelled'];
  for (let i = 1; i <= 80; i++) {
    const lid = synId('leave', i);
    const startOffset = rand(-180, 60);
    const days = rand(1, 10);
    await prisma.leaveRequest.upsert({
      where: { id: lid },
      update: {},
      create: {
        id: lid,
        employeeId: pick(employeeIds),
        type: pick(leaveTypes),
        startDate: daysAgo(-startOffset),
        endDate: daysAgo(-startOffset - days),
        days,
        reason: pick(['Family event','Medical appointment','Personal day','Conference attendance','Vacation travel','Feeling unwell','Moving day','Child care']),
        status: pick(leaveStatuses),
      },
    });
  }
  // Time entries (~200)
  for (let i = 1; i <= 200; i++) {
    const tid = synId('time', i);
    const dayOffset = rand(1, 365);
    const clockInHour = rand(7, 10);
    const hoursWorked = rand(6, 10);
    const d = daysAgo(dayOffset);
    const clockIn = new Date(d); clockIn.setHours(clockInHour, rand(0, 30));
    const clockOut = new Date(clockIn.getTime() + hoursWorked * 3600000);
    await prisma.timeEntry.upsert({
      where: { id: tid },
      update: {},
      create: {
        id: tid,
        employeeId: pick(employeeIds),
        date: d,
        clockIn,
        clockOut,
        breakMinutes: rand(0, 60),
        totalHours: hoursWorked,
        type: Math.random() < 0.9 ? 'regular' : 'overtime',
        project: pick(['Platform Core','Mobile App','Data Pipeline','CRM Integration','Website Redesign','Internal Tools','Client Portal','API Gateway']),
        status: pick(['approved','pending','submitted']),
      },
    });
  }
  console.log('  ✓ HR bank accounts, leave requests, time entries');

  // ╔═══════════════════════════════╗
  // ║  5. PAYROLL RUNS & PAYMENTS  ║
  // ╚═══════════════════════════════╝
  console.log('💵 Payroll...');
  const payrollRunIds: string[] = [];
  for (let m = 0; m < 12; m++) {
    const rid = synId('pr', m + 1);
    const periodStart = new Date(2025, 3 + m, 1);
    const periodEnd = new Date(2025, 4 + m, 0);
    const empCount = rand(42, 50);
    const totalGross = empCount * rand(6000, 12000);
    const totalDed = Math.round(totalGross * 0.28);
    const r = await prisma.payrollRun.upsert({
      where: { runNumber: `PR-2025-${String(m + 1).padStart(2, '0')}` },
      update: {},
      create: {
        id: rid,
        runNumber: `PR-2025-${String(m + 1).padStart(2, '0')}`,
        period: `${periodStart.toISOString().slice(0, 7)}`,
        periodStart,
        periodEnd,
        status: m < 11 ? 'completed' : 'processing',
        totalGross,
        totalDeductions: totalDed,
        totalNet: totalGross - totalDed,
        employeeCount: empCount,
        processedAt: m < 11 ? periodEnd : undefined,
        completedAt: m < 11 ? new Date(periodEnd.getTime() + 2 * DAY) : undefined,
      },
    });
    payrollRunIds.push(r.id);
  }
  // Payments (~600)
  let ppIdx = 0;
  for (const runId of payrollRunIds) {
    const subset = employeeIds.slice(0, rand(42, Math.min(50, employeeIds.length)));
    for (const empId of subset) {
      ppIdx++;
      const pid = synId('pp', ppIdx);
      const gross = money(4000, 15000);
      const net = Math.round(gross * 0.72 * 100) / 100;
      await prisma.payrollPayment.upsert({
        where: { id: pid },
        update: {},
        create: {
          id: pid,
          runId,
          employeeId: empId,
          grossAmount: gross,
          deductions: { federal: Math.round(gross * 0.15), state: Math.round(gross * 0.05), fica: Math.round(gross * 0.0765), health: rand(200, 600) },
          netAmount: net,
          status: 'completed',
          processedAt: daysAgo(rand(1, 365)),
        },
      });
    }
  }
  console.log(`  ✓ ${payrollRunIds.length} payroll runs, ${ppIdx} payments`);

  // PayrollRecords (monthly per employee, 12 months)
  let prIdx = 0;
  for (let m = 1; m <= 12; m++) {
    for (let e = 0; e < Math.min(48, employeeIds.length); e++) {
      prIdx++;
      const prid = synId('prec', prIdx);
      const base = money(4000, 15000);
      const ot = Math.random() < 0.3 ? money(200, 1500) : 0;
      const bonus = Math.random() < 0.1 ? money(500, 5000) : 0;
      const ded = Math.round(base * 0.08);
      const tax = Math.round((base + ot + bonus) * 0.22);
      await prisma.payrollRecord.upsert({
        where: { id: prid },
        update: {},
        create: {
          id: prid,
          employeeId: employeeIds[e],
          period: `2025-${String(m).padStart(2, '0')}`,
          baseSalary: base,
          overtime: ot,
          deductions: ded,
          bonuses: bonus,
          taxWithheld: tax,
          netPay: Math.round((base + ot + bonus - ded - tax) * 100) / 100,
          status: m < 12 ? 'paid' : 'pending',
        },
      });
    }
  }
  console.log(`  ✓ ${prIdx} payroll records`);

  // ╔═══════════════════════════════╗
  // ║  6. CUSTOMERS (~25)           ║
  // ╚═══════════════════════════════╝
  console.log('🤝 Customers & Contacts...');
  const customerIds: string[] = [];
  for (let i = 1; i <= 25; i++) {
    const cid = synId('cust', i);
    const cnum = `CUST-S${String(i).padStart(4, '0')}`;
    const r = await prisma.customer.upsert({
      where: { customerNumber: cnum },
      update: {},
      create: {
        id: cid,
        customerNumber: cnum,
        name: COMPANY_NAMES[i - 1] || `SynCorp ${i}`,
        type: 'company',
        industry: pick(INDUSTRIES),
        email: `billing@${COMPANY_NAMES[i - 1]?.toLowerCase().replace(/[^a-z]/g, '') || `syncorp${i}`}.com`,
        phone: `555-${rand(1000, 9999)}`,
        website: `https://${COMPANY_NAMES[i - 1]?.toLowerCase().replace(/[^a-z]/g, '') || `syncorp${i}`}.com`,
        paymentTerms: pick(['Net 15', 'Net 30', 'Net 45', 'Net 60']),
        creditLimit: rand(25000, 500000),
        status: Math.random() < 0.9 ? 'active' : 'inactive',
      },
    });
    customerIds.push(r.id);
    // Primary contact
    const contId = synId('cont', i);
    const fn = FIRST_NAMES[i + 20];
    const ln = LAST_NAMES[i + 20];
    await prisma.contact.upsert({
      where: { id: contId },
      update: {},
      create: {
        id: contId,
        customerId: r.id,
        firstName: fn,
        lastName: ln,
        email: `${fn.toLowerCase()}.${ln.toLowerCase()}@${COMPANY_NAMES[i - 1]?.toLowerCase().replace(/[^a-z]/g, '') || `syncorp${i}`}.com`,
        phone: `555-${rand(1000, 9999)}`,
        title: pick(['VP Operations','Procurement Director','CTO','IT Manager','CFO','Director of Engineering','Head of Product']),
        isPrimary: true,
      },
    });
  }
  console.log(`  ✓ ${customerIds.length} customers with contacts`);

  // ╔═══════════════════════════════╗
  // ║  7. LEADS (~40)               ║
  // ╚═══════════════════════════════╝
  console.log('🎯 Leads...');
  for (let i = 1; i <= 40; i++) {
    const lid = synId('lead', i);
    const fn = FIRST_NAMES[i + 40];
    const ln = LAST_NAMES[i + 40];
    await prisma.lead.upsert({
      where: { id: lid },
      update: {},
      create: {
        id: lid,
        companyName: `${pick(['Alpha','Beta','Gamma','Delta','Omega','Sigma','Zeta','Theta','Nova','Apex'])} ${pick(['Systems','Labs','Tech','Solutions','Group','Corp','Industries','Dynamics','Networks','Ventures'])}`,
        contactName: `${fn} ${ln}`,
        email: `${fn.toLowerCase()}.${ln.toLowerCase()}@lead${i}.com`,
        phone: `555-${rand(1000, 9999)}`,
        source: pick(LEAD_SOURCES),
        status: pick(['new','contacted','qualified','proposal','negotiation','lost','converted']),
        rating: pick(['hot','warm','cold']),
        industry: pick(INDUSTRIES),
        estimatedValue: rand(10000, 250000),
        customerId: Math.random() < 0.15 ? pick(customerIds) : undefined,
      },
    });
  }
  console.log('  ✓ 40 leads');

  // ╔═══════════════════════════════╗
  // ║  8. OPPORTUNITIES (~30)       ║
  // ╚═══════════════════════════════╝
  console.log('💎 Opportunities...');
  const opportunityIds: string[] = [];
  const stages = ['qualification','discovery','proposal','negotiation','closed-won','closed-lost'];
  for (let i = 1; i <= 30; i++) {
    const oid = synId('opp', i);
    const stage = pick(stages);
    const prob = stage === 'closed-won' ? 100 : stage === 'closed-lost' ? 0 : rand(10, 90);
    const r = await prisma.opportunity.upsert({
      where: { id: oid },
      update: {},
      create: {
        id: oid,
        name: `${pick(COMPANY_NAMES)} — ${pick(['Platform Migration','Annual License Renewal','Enterprise Upgrade','Digital Transformation','Cloud Migration','Security Audit','Data Analytics Suite','API Integration','Custom Development','Managed Services'])}`,
        customerId: pick(customerIds),
        stage,
        probability: prob,
        expectedValue: rand(15000, 350000),
        expectedClose: stage.startsWith('closed') ? daysAgo(rand(1, 180)) : daysFromNow(rand(7, 120)),
        description: 'Synthetic opportunity for report stress testing',
        nextStep: stage.startsWith('closed') ? undefined : pick(['Schedule demo','Send proposal','Contract review','Technical evaluation','Executive meeting','POC setup']),
      },
    });
    opportunityIds.push(r.id);
  }
  console.log(`  ✓ ${opportunityIds.length} opportunities`);

  // ╔═══════════════════════════════╗
  // ║  9. SALES ORDERS (~50)        ║
  // ╚═══════════════════════════════╝
  console.log('📋 Sales Orders...');
  const salesOrderIds: string[] = [];
  for (let i = 1; i <= 50; i++) {
    const soId = synId('so', i);
    const orderNum = `SO-S${String(i).padStart(4, '0')}`;
    const subtotal = money(2000, 75000);
    const tax = Math.round(subtotal * 0.08 * 100) / 100;
    const shipping = rand(0, 500);
    const status = pick(['draft','processing','shipped','delivered','cancelled']);
    const r = await prisma.salesOrder.upsert({
      where: { orderNumber: orderNum },
      update: {},
      create: {
        id: soId,
        orderNumber: orderNum,
        customerId: pick(customerIds),
        status,
        orderDate: daysAgo(rand(1, 365)),
        requiredDate: daysFromNow(rand(-30, 60)),
        subtotal,
        tax,
        shipping,
        total: Math.round((subtotal + tax + shipping) * 100) / 100,
        paymentStatus: pick(['unpaid','partial','paid']),
        paymentMethod: pick(['wire','credit-card','ach','check']),
      },
    });
    salesOrderIds.push(r.id);
    // 1-3 line items per order
    const lineCount = rand(1, 3);
    for (let li = 0; li < lineCount; li++) {
      const liId = synId('soi', (i - 1) * 3 + li + 1);
      const qty = rand(1, 50);
      const price = money(200, 5000);
      await prisma.salesOrderItem.upsert({
        where: { id: liId },
        update: {},
        create: {
          id: liId,
          salesOrderId: r.id,
          description: pick(['Enterprise License','Implementation Services','Annual Support','Training Package','Consulting Hours','Data Migration','Custom Integration','Platform Access']),
          quantity: qty,
          unitPrice: price,
          total: Math.round(qty * price * 100) / 100,
          sortOrder: li,
        },
      });
    }
  }
  console.log(`  ✓ ${salesOrderIds.length} sales orders with line items`);

  // ╔═══════════════════════════════╗
  // ║  10. PRODUCTS & INVENTORY    ║
  // ╚═══════════════════════════════╝
  console.log('📦 Products & Inventory...');
  const productIds: string[] = [];
  const PRODUCT_LIST = [
    { sku: 'SYN-SVC-001', name: 'Platform License (Annual)', cat: 'Software', cost: 5000, sale: 12000 },
    { sku: 'SYN-SVC-002', name: 'Enterprise Support Plan', cat: 'Support', cost: 2000, sale: 6000 },
    { sku: 'SYN-SVC-003', name: 'Implementation Package', cat: 'Services', cost: 8000, sale: 20000 },
    { sku: 'SYN-SVC-004', name: 'Custom Integration (per project)', cat: 'Services', cost: 3000, sale: 8000 },
    { sku: 'SYN-SVC-005', name: 'Data Migration Service', cat: 'Services', cost: 4000, sale: 10000 },
    { sku: 'SYN-SVC-006', name: 'Training Workshop (per day)', cat: 'Training', cost: 500, sale: 2000 },
    { sku: 'SYN-SVC-007', name: 'Consulting (per hour)', cat: 'Consulting', cost: 100, sale: 250 },
    { sku: 'SYN-HW-001', name: 'Edge Gateway Device', cat: 'Hardware', cost: 450, sale: 899 },
    { sku: 'SYN-HW-002', name: 'IoT Sensor Kit', cat: 'Hardware', cost: 120, sale: 299 },
    { sku: 'SYN-HW-003', name: 'Rack Server Unit', cat: 'Hardware', cost: 2500, sale: 4999 },
  ];
  for (const p of PRODUCT_LIST) {
    const r = await prisma.product.upsert({
      where: { sku: p.sku },
      update: {},
      create: {
        sku: p.sku,
        name: p.name,
        category: p.cat,
        costPrice: p.cost,
        salePrice: p.sale,
        reorderLevel: rand(5, 20),
        reorderQuantity: rand(10, 50),
        isActive: true,
      },
    });
    productIds.push(r.id);
    // Inventory item
    const iiId = synId('inv', PRODUCT_LIST.indexOf(p) + 1);
    const qty = rand(5, 100);
    const reserved = rand(0, Math.min(10, qty));
    await prisma.inventoryItem.upsert({
      where: { id: iiId },
      update: {},
      create: {
        id: iiId,
        productId: r.id,
        quantity: qty,
        availableQty: qty - reserved,
        reservedQty: reserved,
        status: qty > 15 ? 'in-stock' : qty > 0 ? 'low-stock' : 'out-of-stock',
      },
    });
  }
  // Stock movements (~60)
  for (let i = 1; i <= 60; i++) {
    const smId = synId('sm', i);
    const qty = rand(1, 50);
    await prisma.stockMovement.upsert({
      where: { id: smId },
      update: {},
      create: {
        id: smId,
        productId: pick(productIds),
        type: pick(['in', 'out', 'adjustment']),
        quantity: qty,
        reference: `REF-${rand(1000, 9999)}`,
        reason: pick(['Purchase Order','Customer Order','Damage','Return','Cycle Count Adjustment','Transfer']),
        createdAt: daysAgo(rand(1, 365)),
      },
    });
  }
  console.log(`  ✓ ${PRODUCT_LIST.length} products, inventory items, 60 stock movements`);

  // ╔═══════════════════════════════╗
  // ║  11. VENDORS & VENDOR INV    ║
  // ╚═══════════════════════════════╝
  console.log('🏭 Vendors & Vendor Invoices...');
  const vendorIds: string[] = [];
  for (let i = 1; i <= 15; i++) {
    const vid = synId('vend', i);
    const r = await prisma.vendor.upsert({
      where: { id: vid },
      update: {},
      create: {
        id: vid,
        name: VENDOR_NAMES[i - 1],
        contactEmail: `billing@${VENDOR_NAMES[i - 1].toLowerCase().replace(/[^a-z]/g, '')}.com`,
        contactPhone: `555-${rand(1000, 9999)}`,
        category: pick(['technology','office','telecom','marketing','legal','staffing','facilities','insurance']),
        paymentTerms: pick(['net-15','net-30','net-45','net-60']),
        isActive: true,
      },
    });
    vendorIds.push(r.id);
  }
  // Vendor invoices (~60)
  for (let i = 1; i <= 60; i++) {
    const viNum = `VI-S${String(i).padStart(4, '0')}`;
    const amt = money(500, 25000);
    await prisma.vendorInvoice.upsert({
      where: { invoiceNumber: viNum },
      update: {},
      create: {
        invoiceNumber: viNum,
        vendorId: pick(vendorIds),
        amount: amt,
        dueDate: daysFromNow(rand(-30, 60)),
        status: pick(['pending','approved','scheduled','paid','overdue']),
        category: pick(['software','hardware','services','rent','utilities','marketing','legal','insurance']),
        description: pick(['Monthly SaaS subscription','Hardware purchase','Consulting fee','Office rent','Insurance premium','Marketing campaign','Legal retainer','Cloud hosting']),
      },
    });
  }
  console.log(`  ✓ ${vendorIds.length} vendors, 60 vendor invoices`);

  // ╔═══════════════════════════════╗
  // ║  12. INVOICES (~200)          ║
  // ╚═══════════════════════════════╝
  console.log('🧾 Invoices...');
  for (let i = 1; i <= 200; i++) {
    const invNum = `INV-S${String(i).padStart(4, '0')}`;
    const isCustomer = Math.random() < 0.75;
    const subtotal = money(500, 50000);
    const tax = Math.round(subtotal * 0.08 * 100) / 100;
    const total = Math.round((subtotal + tax) * 100) / 100;
    const status = pick(['draft','sent','paid','overdue','cancelled','partial']);
    const paid = status === 'paid' ? total : status === 'partial' ? Math.round(total * Math.random() * 100) / 100 : 0;
    await prisma.invoice.upsert({
      where: { invoiceNumber: invNum },
      update: {},
      create: {
        invoiceNumber: invNum,
        type: isCustomer ? 'customer' : 'vendor',
        customerId: isCustomer ? pick(customerIds) : undefined,
        vendorId: !isCustomer ? pick(vendorIds) : undefined,
        issueDate: daysAgo(rand(1, 365)),
        dueDate: daysFromNow(rand(-30, 60)),
        status,
        subtotal,
        tax,
        total,
        amountPaid: paid,
        items: [
          { description: pick(['Consulting Services','Software License','Support Contract','Training','Implementation','Hardware','Integration']), qty: rand(1, 50), rate: money(100, 5000), amount: subtotal },
        ],
      },
    });
  }
  console.log('  ✓ 200 invoices');

  // ╔═══════════════════════════════╗
  // ║  13. TRANSACTIONS (~500)      ║
  // ╚═══════════════════════════════╝
  console.log('📊 Transactions...');
  const acctIds = Object.values(accountMap);
  for (let i = 1; i <= 500; i++) {
    const tid = synId('txn', i);
    const type = pick(['receipt','payment','journal','transfer']);
    const isDebit = type === 'payment';
    const amt = money(100, 30000) * (isDebit ? -1 : 1);
    await prisma.transaction.upsert({
      where: { id: tid },
      update: {},
      create: {
        id: tid,
        accountId: pick(acctIds),
        type,
        description: pick([
          'Customer payment received','Vendor payment','Payroll disbursement','SaaS subscription',
          'Office rent','Insurance premium','Equipment purchase','Consulting revenue',
          'Training revenue','Refund issued','Bank transfer','Interest income',
          'Marketing expense','Travel reimbursement','Utility payment','Software license'
        ]),
        amount: amt,
        status: pick(['posted','pending','reconciled']),
        date: daysAgo(rand(1, 365)),
      },
    });
  }
  console.log('  ✓ 500 transactions');

  // ╔═══════════════════════════════╗
  // ║  14. BUDGETS & EXPENSES      ║
  // ╚═══════════════════════════════╝
  console.log('💰 Budgets & Expenses...');
  const deptIdValues = Object.values(deptMap);
  for (let q = 1; q <= 4; q++) {
    for (let d = 0; d < deptIdValues.length; d++) {
      const bid = synId('bud', (q - 1) * deptIdValues.length + d + 1);
      const amount = rand(20000, 150000);
      const spent = Math.round(amount * Math.random() * 0.95);
      await prisma.budget.upsert({
        where: { id: bid },
        update: {},
        create: {
          id: bid,
          name: `Q${q} 2025 — ${DEPARTMENTS_DEF[d % DEPARTMENTS_DEF.length].name}`,
          departmentId: deptIdValues[d],
          period: 'quarterly',
          startDate: new Date(2025, (q - 1) * 3, 1),
          endDate: new Date(2025, q * 3, 0),
          amount,
          spent,
          remaining: amount - spent,
          status: q < 4 ? 'closed' : 'active',
        },
      });
    }
  }
  // Expenses (~120)
  for (let i = 1; i <= 120; i++) {
    const eid = synId('exp', i);
    await prisma.expense.upsert({
      where: { id: eid },
      update: {},
      create: {
        id: eid,
        description: pick(['Cloud hosting','Office supplies','Software subscription','Travel — client site','Conference registration','Team lunch','Equipment repair','Marketing ads','Legal consultation','Insurance premium']),
        amount: money(50, 8000),
        date: daysAgo(rand(1, 365)),
        category: pick(['technology','facilities','travel','marketing','legal','insurance','utilities','office']),
        vendor: pick(VENDOR_NAMES),
        paymentMethod: pick(['corporate-card','wire','ach','check','petty-cash']),
        status: pick(['approved','pending','rejected','reimbursed']),
      },
    });
  }
  console.log('  ✓ budgets & expenses');

  // ╔═══════════════════════════════╗
  // ║  15. CONTRACTS                ║
  // ╚═══════════════════════════════╝
  console.log('📝 Contracts...');
  for (let i = 1; i <= 20; i++) {
    const cid = synId('ctr', i);
    const cnum = `CTR-SYN-${String(i).padStart(3, '0')}`;
    const isVendor = Math.random() < 0.5;
    const party = isVendor ? pick(VENDOR_NAMES) : pick(COMPANY_NAMES);
    const startDay = rand(180, 730);
    await prisma.contract.upsert({
      where: { contractNumber: cnum },
      update: {},
      create: {
        id: cid,
        contractNumber: cnum,
        title: `${party} — ${pick(['Service Agreement','License Agreement','NDA','SLA','Master Agreement','Statement of Work','Lease Agreement'])}`,
        type: isVendor ? 'vendor' : 'customer',
        partyName: party,
        partyType: isVendor ? 'vendor' : 'customer',
        startDate: daysAgo(startDay),
        endDate: daysFromNow(rand(-30, 365)),
        value: rand(5000, 200000),
        status: pick(['active','expired','pending','terminated']),
        autoRenew: Math.random() < 0.6,
      },
    });
  }
  console.log('  ✓ 20 contracts');

  // ╔═══════════════════════════════╗
  // ║  16. AGENTS & WORKFLOWS      ║
  // ╚═══════════════════════════════╝
  console.log('🤖 Agents, Workflows, Tasks...');
  const agentIds: string[] = [];
  const AGENTS = [
    { name: 'Finance Agent', type: 'domain', desc: 'Handles AR/AP, invoicing, reconciliation' },
    { name: 'HR Agent', type: 'domain', desc: 'Payroll, leave management, onboarding' },
    { name: 'Sales Agent', type: 'domain', desc: 'Pipeline management, forecasting' },
    { name: 'Ops Agent', type: 'domain', desc: 'Inventory, fulfillment, vendor management' },
    { name: 'Security Agent', type: 'domain', desc: 'Access control, threat detection' },
    { name: 'Analytics Agent', type: 'analytics', desc: 'Reporting, dashboards, data analysis' },
    { name: 'Compliance Agent', type: 'domain', desc: 'Policy enforcement, audit trails' },
    { name: 'Orchestrator', type: 'orchestrator', desc: 'Coordinates cross-domain workflows' },
  ];
  for (let i = 0; i < AGENTS.length; i++) {
    const aid = synId('agent', i + 1);
    const r = await prisma.agent.upsert({
      where: { id: aid },
      update: {},
      create: {
        id: aid,
        name: AGENTS[i].name,
        description: AGENTS[i].desc,
        type: AGENTS[i].type,
        status: 'active',
      },
    });
    agentIds.push(r.id);
  }

  const workflowIds: string[] = [];
  const WORKFLOWS = [
    { name: 'Invoice-to-Cash', trigger: { type: 'event', event: 'invoice.created' } },
    { name: 'Employee Onboarding', trigger: { type: 'event', event: 'employee.created' } },
    { name: 'Monthly Close', trigger: { type: 'schedule', cron: '0 0 1 * *' } },
    { name: 'Security Audit', trigger: { type: 'schedule', cron: '0 6 * * 1' } },
    { name: 'Inventory Reorder', trigger: { type: 'threshold', metric: 'stock_level' } },
  ];
  for (let i = 0; i < WORKFLOWS.length; i++) {
    const wid = synId('wf', i + 1);
    const r = await prisma.workflow.upsert({
      where: { id: wid },
      update: {},
      create: {
        id: wid,
        name: WORKFLOWS[i].name,
        trigger: WORKFLOWS[i].trigger,
        steps: [{ step: 1, action: 'validate' }, { step: 2, action: 'process' }, { step: 3, action: 'notify' }],
        status: 'active',
      },
    });
    workflowIds.push(r.id);
  }

  // Workflow executions (~30)
  const execIds: string[] = [];
  for (let i = 1; i <= 30; i++) {
    const eid = synId('wfe', i);
    const r = await prisma.workflowExecution.upsert({
      where: { id: eid },
      update: {},
      create: {
        id: eid,
        workflowId: pick(workflowIds),
        status: pick(['completed','running','failed','cancelled']),
        currentStep: `step-${rand(1, 3)}`,
        startedAt: daysAgo(rand(1, 180)),
        completedAt: Math.random() < 0.7 ? daysAgo(rand(0, 179)) : undefined,
      },
    });
    execIds.push(r.id);
  }

  // Tasks (~100)
  for (let i = 1; i <= 100; i++) {
    const taskId = synId('task', i);
    await prisma.task.upsert({
      where: { id: taskId },
      update: {},
      create: {
        id: taskId,
        executionId: Math.random() < 0.6 ? pick(execIds) : undefined,
        agentId: pick(agentIds),
        type: pick(['data_fetch','validation','notification','processing','approval','reconciliation','analysis','report']),
        input: { source: 'synthetic', batchId: rand(1, 500) },
        status: pick(['completed','running','failed','queued','cancelled']),
        output: { result: 'ok', records: rand(1, 200) },
        priority: rand(1, 100),
        startedAt: daysAgo(rand(1, 180)),
        completedAt: Math.random() < 0.75 ? daysAgo(rand(0, 179)) : undefined,
      },
    });
  }
  console.log(`  ✓ ${AGENTS.length} agents, ${WORKFLOWS.length} workflows, 30 executions, 100 tasks`);

  // ╔═══════════════════════════════╗
  // ║  17. SECURITY EVENTS (~100)  ║
  // ╚═══════════════════════════════╝
  console.log('🔒 Security Events...');
  const secTypes = ['login_success','login_failure','permission_denied','mfa_triggered','suspicious_ip','data_export','config_change','api_abuse','password_reset','session_expired'];
  for (let i = 1; i <= 100; i++) {
    const sid = synId('sec', i);
    const type = pick(secTypes);
    const severity = type.includes('failure') || type.includes('abuse') ? 'high' : type.includes('suspicious') ? 'critical' : pick(['low','medium','high']);
    await prisma.securityEvent.upsert({
      where: { id: sid },
      update: {},
      create: {
        id: sid,
        type,
        severity,
        description: `${type.replace(/_/g, ' ')} event detected`,
        ipAddress: `${rand(10, 200)}.${rand(0, 255)}.${rand(0, 255)}.${rand(1, 254)}`,
        detectedBy: pick(['system','agent','manual','firewall','ids']),
        status: pick(['open','investigating','resolved','dismissed']),
        timestamp: daysAgo(rand(1, 365)),
      },
    });
  }
  console.log('  ✓ 100 security events');

  // ╔═══════════════════════════════╗
  // ║  18. HEALTH METRICS (~50)    ║
  // ╚═══════════════════════════════╝
  console.log('💚 Health Metrics...');
  const components = ['database','api-gateway','auth-service','payment-processor','cache','queue','search','email-service','storage','cdn'];
  for (let i = 1; i <= 50; i++) {
    const hid = synId('hm', i);
    const comp = pick(components);
    const healthy = Math.random() < 0.85;
    await prisma.healthMetric.upsert({
      where: { id: hid },
      update: {},
      create: {
        id: hid,
        component: comp,
        componentName: comp.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
        status: healthy ? 'healthy' : pick(['degraded','unhealthy','maintenance']),
        statusMessage: healthy ? 'All systems operational' : pick(['High latency detected','Error rate elevated','Scheduled maintenance','Connection pool exhausted']),
        latency: rand(5, healthy ? 200 : 2000),
        uptime: healthy ? 99 + Math.random() : 95 + Math.random() * 4,
        memoryUsage: Math.random() * 80 + (healthy ? 0 : 15),
        cpuUsage: Math.random() * 60 + (healthy ? 0 : 25),
        errorRate: healthy ? Math.random() * 0.5 : Math.random() * 5,
        requestCount: rand(100, 50000),
        timestamp: daysAgo(rand(0, 30)),
      },
    });
  }
  console.log('  ✓ 50 health metrics');

  // ╔═══════════════════════════════╗
  // ║  19. AUDIT LOG (~200)        ║
  // ╚═══════════════════════════════╝
  console.log('📜 Audit Logs...');
  const actions = ['create','update','delete','view','export','approve','reject','login','logout','config_change'];
  const resources = ['invoice','employee','customer','transaction','payroll','order','product','workflow','report','setting'];
  for (let i = 1; i <= 200; i++) {
    const aid = synId('audit', i);
    await prisma.auditLog.upsert({
      where: { id: aid },
      update: {},
      create: {
        id: aid,
        agentId: Math.random() < 0.6 ? pick(agentIds) : undefined,
        action: pick(actions),
        resource: pick(resources),
        resourceId: `res-${rand(1, 500)}`,
        details: { ip: `${rand(10, 200)}.${rand(0, 255)}.${rand(0, 255)}.${rand(1, 254)}`, synthetic: true },
        timestamp: daysAgo(rand(1, 365)),
      },
    });
  }
  console.log('  ✓ 200 audit log entries');

  // ═══════════════════════════════════════════════════════════════════════
  console.log('\n✅ ════════════════════════════════════════════');
  console.log('   SYNTHETIC SEED COMPLETE');
  console.log('   All report-targeted tables populated.');
  console.log('════════════════════════════════════════════\n');
}

main()
  .catch((e) => {
    console.error('❌ Synthetic seed failed:', e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
