/**
 * Shared seed + drive helpers for the end-to-end sales-journey specs.
 *
 * These drive the JSON API directly (`page.request.*`) with the
 * `X-Port-Id` header the `withAuth` helper requires (see
 * `tests/e2e/smoke/helpers.ts → apiHeaders`). They give the
 * exhaustive (deterministic) and realapi (Documenso-gated) journey
 * specs a single source of truth for building a fully-loaded interest
 * with a priced primary berth — the minimum a deal needs before it can
 * legally leave the Enquiry stage and walk the pipeline.
 *
 * Pipeline stage machine lives in `src/lib/constants.ts`
 * (`PIPELINE_STAGES` / `STAGE_TRANSITIONS` / `canTransitionStage`):
 *
 *   enquiry → qualified → nurturing → eoi → reservation
 *     → deposit_paid → contract
 *
 * Gates encoded by `changeInterestStage`:
 *   - a yacht must be linked before leaving `enquiry`;
 *   - the primary berth must carry a non-zero `price` before entering
 *     any of eoi / reservation / deposit_paid / contract.
 */
import { type APIRequestContext, type Page, expect } from '@playwright/test';

import { apiHeaders } from '../smoke/helpers';

export interface SeededDeal {
  clientId: string;
  yachtId: string;
  berthId: string;
  interestId: string;
  mooringNumber: string;
}

interface DataEnvelope<T> {
  data: T;
}

/**
 * Seed a client + yacht + priced berth + interest, all wired together,
 * via the v1 JSON API. The interest is created with the berth as its
 * primary link (the create path materialises a primary `interest_berths`
 * row), and the berth carries a real price so the deal can advance past
 * Qualified.
 */
export async function seedFullyLoadedDeal(page: Page, label: string): Promise<SeededDeal> {
  const headers = await apiHeaders(page);
  const stamp = Date.now();
  // Mooring numbers must match the canonical `^[A-Z]+\d+$` form; use a
  // high prefix so we never collide with the global-setup seed berths
  // (A1, A2, B1).
  const mooringNumber = `Z${(stamp % 9000) + 1000}`;

  const clientRes = await page.request.post('/api/v1/clients', {
    headers,
    data: {
      fullName: `${label} Client ${stamp}`,
      contacts: [{ channel: 'email', value: `journey-${stamp}@example.test`, isPrimary: true }],
    },
  });
  expect(clientRes.ok(), `client create: ${clientRes.status()} ${await clientRes.text()}`).toBe(
    true,
  );
  const clientId = ((await clientRes.json()) as DataEnvelope<{ id: string }>).data.id;

  const yachtRes = await page.request.post('/api/v1/yachts', {
    headers,
    data: {
      name: `${label} Yacht ${stamp}`,
      owner: { type: 'client', id: clientId },
    },
  });
  expect(yachtRes.ok(), `yacht create: ${yachtRes.status()} ${await yachtRes.text()}`).toBe(true);
  const yachtId = ((await yachtRes.json()) as DataEnvelope<{ id: string }>).data.id;

  const berthRes = await page.request.post('/api/v1/berths', {
    headers,
    data: {
      mooringNumber,
      area: 'Journey Area',
      // Priced so the interest can leave Qualified — `changeInterestStage`
      // rejects a $0 primary berth on any priced stage.
      price: 250000,
      priceCurrency: 'EUR',
      tenureType: 'permanent',
    },
  });
  expect(berthRes.ok(), `berth create: ${berthRes.status()} ${await berthRes.text()}`).toBe(true);
  const berthId = ((await berthRes.json()) as DataEnvelope<{ id: string }>).data.id;

  const interestRes = await page.request.post('/api/v1/interests', {
    headers,
    data: { clientId, yachtId, berthId, pipelineStage: 'enquiry' },
  });
  expect(
    interestRes.ok(),
    `interest create: ${interestRes.status()} ${await interestRes.text()}`,
  ).toBe(true);
  const interestId = ((await interestRes.json()) as DataEnvelope<{ id: string }>).data.id;

  return { clientId, yachtId, berthId, interestId, mooringNumber };
}

/**
 * Drive a single stage transition through the canonical `/stage` PATCH
 * route and assert the interest now reports the requested stage. Mirrors
 * the UI: the route enforces `canTransitionStage` + the priced-berth +
 * yacht gates, so an illegal jump surfaces as a 4xx the caller can assert
 * on separately.
 */
export async function changeStage(
  request: APIRequestContext,
  headers: Record<string, string>,
  interestId: string,
  pipelineStage: string,
): Promise<void> {
  const res = await request.patch(`/api/v1/interests/${interestId}/stage`, {
    headers,
    data: { pipelineStage },
  });
  expect(res.ok(), `stage → ${pipelineStage}: ${res.status()} ${await res.text()}`).toBe(true);
}

/** Fetch the current pipeline stage for an interest via the read API. */
export async function readStage(
  request: APIRequestContext,
  headers: Record<string, string>,
  interestId: string,
): Promise<string> {
  const res = await request.get(`/api/v1/interests/${interestId}`, { headers });
  expect(res.ok(), `read interest: ${res.status()}`).toBe(true);
  const body = (await res.json()) as DataEnvelope<{ pipelineStage: string }>;
  return body.data.pipelineStage;
}
