import { describe, expect, it, vi } from "vitest";
import { updatePibiRequest, PIBI_ORDERED_TEXT_PROP } from "./updatePibiRequest";
import type { RunUpdatePibiTx, SyncMeasuresField } from "./updatePibiRequest";

function makeDeps(overrides?: {
  txResult?: { allMeasureNames: string[] };
  txError?: Error;
  syncResult?: { ok: true } | { ok: false; error: string };
}) {
  const txResult = overrides?.txResult ?? { allMeasureNames: ["CWI", "ASHP"] };

  const runUpdateTx: RunUpdatePibiTx = vi.fn(async () => {
    if (overrides?.txError) throw overrides.txError;
    return txResult;
  });

  const syncMeasuresField: SyncMeasuresField = vi.fn(async () => {
    return overrides?.syncResult ?? ({ ok: true } as const);
  });

  return { runUpdateTx, syncMeasuresField };
}

describe("updatePibiRequest — happy path", () => {
  it("updates row, re-syncs all deal measures to HubSpot", async () => {
    const completedAt = new Date("2026-05-10T12:00:00Z");
    const deps = makeDeps({
      txResult: { allMeasureNames: ["CWI", "ASHP"] },
    });

    const result = await updatePibiRequest({
      id: 7n,
      dealId: "deal-1",
      updates: { completedAt },
      deps,
    });

    expect(result).toEqual({ ok: true, hubspotSync: "ok" });

    expect(deps.runUpdateTx).toHaveBeenCalledWith({
      id: 7n,
      dealId: "deal-1",
      updates: { completedAt },
    });

    expect(deps.syncMeasuresField).toHaveBeenCalledWith({
      hubspotDealId: "deal-1",
      propName: PIBI_ORDERED_TEXT_PROP,
      measureNames: ["CWI", "ASHP"],
    });
  });

  it("can update measureName, orderedAt, and completedAt", async () => {
    const orderedAt = new Date("2026-05-01T09:00:00Z");
    const deps = makeDeps();

    await updatePibiRequest({
      id: 3n,
      dealId: "deal-2",
      updates: { measureName: "EWI", orderedAt },
      deps,
    });

    expect(deps.runUpdateTx).toHaveBeenCalledWith({
      id: 3n,
      dealId: "deal-2",
      updates: { measureName: "EWI", orderedAt },
    });
  });
});

describe("updatePibiRequest — DB failure", () => {
  it("returns ok=false, skips HubSpot when tx throws", async () => {
    const deps = makeDeps({ txError: new Error("row not found") });

    const result = await updatePibiRequest({
      id: 99n,
      dealId: "deal-x",
      updates: { completedAt: new Date() },
      deps,
    });

    expect(result).toEqual({ ok: false, error: "row not found" });
    expect(deps.syncMeasuresField).not.toHaveBeenCalled();
  });
});

describe("updatePibiRequest — HubSpot failure", () => {
  it("returns ok=true with hubspotSync=failed", async () => {
    const deps = makeDeps({
      syncResult: { ok: false, error: "hubspot timeout" },
    });

    const result = await updatePibiRequest({
      id: 5n,
      dealId: "deal-h",
      updates: { completedAt: new Date() },
      deps,
    });

    expect(result).toMatchObject({
      ok: true,
      hubspotSync: "failed",
      hubspotError: "hubspot timeout",
    });
  });
});
