import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import { InvalidSyncJobStatusError } from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { baseSyncJob, failedSyncJob } from "../testing/fixtures";
import { run } from "./retrySyncJob";

describe("retrySyncJob", () => {
  it("retries a failed sync job", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const retried = { ...baseSyncJob, retryOfSyncJobId: failedSyncJob.id };
    mock.enqueueResults([failedSyncJob], [retried]);

    const result = await mock.withTx((trx) =>
      run(trx, { syncJobId: failedSyncJob.id }, commandCtx),
    );

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

  it("retries a partially succeeded sync job", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const partiallySucceeded = { ...failedSyncJob, status: "PARTIALLY_SUCCEEDED" as const };
    mock.enqueueResults(
      [partiallySucceeded],
      [{ ...baseSyncJob, retryOfSyncJobId: partiallySucceeded.id }],
    );

    const result = await mock.withTx((trx) =>
      run(trx, { syncJobId: partiallySucceeded.id }, commandCtx),
    );

    expectOk(result);
  });

  it("throws when sync job is still running", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseSyncJob]);

    const result = await mock.withTx((trx) => run(trx, { syncJobId: baseSyncJob.id }, commandCtx));

    expectErr(result, InvalidSyncJobStatusError);
  });

  it("preserves idempotent identity during retry", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const retried = { ...baseSyncJob, retryOfSyncJobId: failedSyncJob.id };
    mock.enqueueResults([failedSyncJob], [retried]);

    const result = await mock.withTx((trx) =>
      run(trx, { syncJobId: failedSyncJob.id }, commandCtx),
    );

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