/**
 * Wide synthetic seed for load + perf testing.
 *
 * Faker-generated bulk client records (default 1000, configurable via
 * `WIDE_SEED_COUNT` env var). Designed to exercise pagination, list-
 * page rendering, search indexing, and analytics aggregations under
 * realistic-ish volumes.
 *
 * Distinct from `seed-synthetic-data.ts` which is hand-curated for
 * every-pipeline-stage end-to-end test coverage. This file is for
 * "what happens when there are 10k rows" stress tests; the curated
 * seed stays the source of truth for selector-keyed Playwright runs.
 *
 * Idempotent at the row-count level: stamps `clients.source_details`
 * with `wide-synthetic` and skips ports already at or above the
 * target count.
 *
 * Run via `pnpm db:seed:wide-synthetic`. Override count via env:
 *   `WIDE_SEED_COUNT=5000 pnpm db:seed:wide-synthetic`
 */

import { faker } from '@faker-js/faker';
import { and, eq, count } from 'drizzle-orm';

import { db } from './index';
import { withTransaction } from './utils';
import {
  clients,
  clientContacts,
  clientAddresses,
  berths,
  interests,
  interestBerths,
} from './schema';
import { PIPELINE_STAGES, type PipelineStage } from '@/lib/constants';

// Stable seed = deterministic output across runs of the same WIDE_SEED_COUNT.
// Bump if the distribution feels stale.
const FAKER_SEED = 20260512;

// Marker stamped on every wide-synthetic client so the idempotency
// check + future cleanup query can find them without colliding with
// either the curated synthetic seed or any real records.
const WIDE_MARKER = 'wide-synthetic';

// Acquisition source distribution roughly matching how a real marina
// funnel breaks down - most opportunity comes through the website, then
// referrals, then brokers, then manual entry. Tweak when product data
// gives us better numbers.
const SOURCE_DISTRIBUTION: Array<{
  source: 'website' | 'referral' | 'broker' | 'manual';
  weight: number;
}> = [
  { source: 'website', weight: 0.55 },
  { source: 'referral', weight: 0.25 },
  { source: 'broker', weight: 0.15 },
  { source: 'manual', weight: 0.05 },
];

function pickWeighted<T extends { weight: number }>(items: T[]): T {
  const r = faker.number.float({ min: 0, max: 1 });
  let acc = 0;
  for (const item of items) {
    acc += item.weight;
    if (r <= acc) return item;
  }
  return items[items.length - 1]!;
}

export interface WideSeedSummary {
  clients: number;
  interests: number;
}

export async function seedWideSyntheticPortData(
  portId: string,
  portSlug: string,
  targetClients: number,
): Promise<WideSeedSummary | null> {
  faker.seed(FAKER_SEED);

  // Idempotency: count existing wide-marker rows. Skip if we'd be at
  // or above the target count.
  const [existing] = await db
    .select({ count: count() })
    .from(clients)
    .where(and(eq(clients.portId, portId), eq(clients.sourceDetails, WIDE_MARKER)));

  if ((existing?.count ?? 0) >= targetClients) {
    return null;
  }

  const portBerths = await db
    .select({ id: berths.id, mooringNumber: berths.mooringNumber })
    .from(berths)
    .where(eq(berths.portId, portId));

  if (portBerths.length === 0) {
    console.warn(`  [${portSlug}] no berths in port - wide seed skipping`);
    return { clients: 0, interests: 0 };
  }

  let clientsInserted = 0;
  let interestsInserted = 0;

  // Chunked transactions: one tx per 100 rows so a mid-run failure
  // doesn't roll back the entire batch and force a from-scratch retry.
  const CHUNK = 100;
  const startIdx = existing?.count ?? 0;

  for (let chunkStart = startIdx; chunkStart < targetClients; chunkStart += CHUNK) {
    const chunkEnd = Math.min(chunkStart + CHUNK, targetClients);

    await withTransaction(async (tx) => {
      for (let i = chunkStart; i < chunkEnd; i++) {
        const sourceChoice = pickWeighted(SOURCE_DISTRIBUTION);
        const firstName = faker.person.firstName();
        const lastName = faker.person.lastName();
        const fullName = `${firstName} ${lastName}`;
        const email = faker.internet.email({ firstName, lastName }).toLowerCase();
        const phone = faker.phone.number({ style: 'international' });
        const country = faker.location.countryCode('alpha-2');
        const city = faker.location.city();
        const street = faker.location.streetAddress();
        const postalCode = faker.location.zipCode();

        const createdDaysAgo = faker.number.int({ min: 1, max: 365 });
        const createdAt = new Date(Date.now() - createdDaysAgo * 86_400_000);

        const [client] = await tx
          .insert(clients)
          .values({
            portId,
            fullName,
            source: sourceChoice.source,
            sourceDetails: WIDE_MARKER,
            nationalityIso: country,
            preferredContactMethod: 'email',
            preferredLanguage: 'en',
            createdAt,
            updatedAt: createdAt,
          })
          .returning({ id: clients.id });

        if (!client) continue;
        clientsInserted++;

        await tx.insert(clientContacts).values([
          {
            clientId: client.id,
            channel: 'email',
            value: email,
            label: 'primary',
            isPrimary: true,
          },
          {
            clientId: client.id,
            channel: 'phone',
            value: phone,
            label: 'primary',
            isPrimary: false,
          },
        ]);

        await tx.insert(clientAddresses).values({
          clientId: client.id,
          portId,
          label: 'Primary',
          streetAddress: street,
          city,
          postalCode,
          countryIso: country,
          isPrimary: true,
        });

        // ~70% of clients get an interest, spread across pipeline stages.
        // The remaining ~30% sit as raw leads (open/no-interest state).
        if (faker.number.float({ min: 0, max: 1 }) < 0.7) {
          const stage = faker.helpers.arrayElement(PIPELINE_STAGES) as PipelineStage;
          const berth = faker.helpers.arrayElement(portBerths);

          const [interest] = await tx
            .insert(interests)
            .values({
              portId,
              clientId: client.id,
              yachtId: null,
              pipelineStage: stage,
              leadCategory:
                stage === 'enquiry'
                  ? 'general_interest'
                  : stage === 'qualified' || stage === 'nurturing'
                    ? 'specific_qualified'
                    : 'hot_lead',
              source: sourceChoice.source,
              createdAt,
              updatedAt: createdAt,
            })
            .returning({ id: interests.id });

          if (interest) {
            interestsInserted++;
            // ~50% of interests link to a berth - late-stage flow needs
            // one, early-stage doesn't have to.
            if (faker.number.float({ min: 0, max: 1 }) < 0.5) {
              await tx.insert(interestBerths).values({
                interestId: interest.id,
                berthId: berth.id,
                isPrimary: true,
                isSpecificInterest: false,
                isInEoiBundle: false,
              });
            }
          }
        }
      }
    });
  }

  return { clients: clientsInserted, interests: interestsInserted };
}
