import { IsInt, IsString, Min } from "class-validator";
import { SagaBuilder } from "./saga-builder";
import { SagaProcessor } from "./saga-processor";

describe("SagaProcessor", () => {
  class TestDto {
    @IsString()
    name: string;
    @IsInt()
    @Min(1)
    count: number;
  }

  it("should execute a simple workflow and enrich context", async () => {
    const builder = new SagaBuilder();
    const workflow = builder
      .step("init")
      .invoke(() => ({ name: "foo", count: 2 }))
      .build();
    const processor = new SagaProcessor();
    processor.add(workflow);
    const result = await processor.start();
    expect(result.state).toBe("success");
    expect(result.context).toEqual({ name: "foo", count: 2 });
    expect(result.errors).toHaveLength(0);
  });

  it("should validate context and fail if invalid", async () => {
    const builder = new SagaBuilder();
    const workflow = builder
      .step("invalid")
      .invoke(() => ({ name: 123, count: 0 }))
      .validate(TestDto)
      .build();
    const processor = new SagaProcessor();
    processor.add(workflow);
    const result = await processor.start();
    expect(result.state).toBe("failed");
    expect(result.errors.length).toBeGreaterThan(0);
    expect(result.errors[0].message).toContain("Validation failed");
  });

  it("should skip step if condition is false", async () => {
    const builder = new SagaBuilder();
    const workflow = builder
      .step("step1")
      .invoke(() => ({ a: 1 }))
      .step("step2")
      .condition(() => false)
      .invoke(() => ({ b: 2 }))
      .build();
    const processor = new SagaProcessor();
    processor.add(workflow);
    const result = await processor.start();
    expect(result.context).toEqual({ a: 1 });
    expect(result.state).toBe("success");
  });

  it("should execute compensation on error", async () => {
    let compensated = false;
    const builder = new SagaBuilder();
    const workflow = builder
      .step("step1")
      .invoke(() => ({ a: 1 }))
      .withCompensation(() => {
        compensated = true;
      })
      .step("step2")
      .invoke(() => {
        throw new Error("fail");
      })
      .build();
    const processor = new SagaProcessor();
    processor.add(workflow);
    const result = await processor.start();
    expect(result.state).toBe("failed");
    expect(compensated).toBe(true);
  });

  it("should inject services and use them in invokes", async () => {
    const log: string[] = [];
    const services = {
      logger: { log: (msg: string) => log.push(msg) },
    };
    const builder = new SagaBuilder();
    const workflow = builder
      .step("log")
      .invoke((ctx, { logger }) => {
        logger.log("hello");
      })
      .build();
    const processor = new SagaProcessor(services);
    processor.add(workflow);
    await processor.start();
    expect(log).toContain("hello");
  });
});
