import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import { InvalidSyncJobStatusError } from "../lib/errors.generated";
import { baseSyncJob, failedSyncJob } from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./retrySyncJob";

describe("retrySyncJob", () => {
  it("retries a failed sync job", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const retried = { ...baseSyncJob, retryOfSyncJobId: failedSyncJob.id };
    spies.select.mockReturnValueOnce(failedSyncJob);
    spies.insert.mockReturnValueOnce(retried);

    const result = await run(db, { syncJobId: failedSyncJob.id }, commandCtx);

    const value = expectOk(result);
    expect(value.syncJob.retryOfSyncJobId).toBe(failedSyncJob.id);
  });

  it("retries a partially succeeded sync job", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const partiallySucceeded = { ...failedSyncJob, status: "PARTIALLY_SUCCEEDED" as const };
    spies.select.mockReturnValueOnce(partiallySucceeded);
    spies.insert.mockReturnValueOnce({ ...baseSyncJob, retryOfSyncJobId: partiallySucceeded.id });

    const result = await run(db, { syncJobId: partiallySucceeded.id }, commandCtx);

    expectOk(result);
  });

  it("throws when sync job is still running", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseSyncJob);

    const result = await run(db, { syncJobId: baseSyncJob.id }, commandCtx);

    expectErr(result, InvalidSyncJobStatusError);
  });

  it("preserves idempotent identity during retry", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const retried = { ...baseSyncJob, retryOfSyncJobId: failedSyncJob.id };
    spies.select.mockReturnValueOnce(failedSyncJob);
    spies.insert.mockReturnValueOnce(retried);

    const result = await run(db, { syncJobId: failedSyncJob.id }, commandCtx);

    const value = expectOk(result);
    expect(value.syncJob.channelId).toBe(failedSyncJob.channelId);
    expect(value.syncJob.companyId).toBe(failedSyncJob.companyId);
  });
});
