import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  ProfitCenterNotFoundError,
  InvalidStateError,
  FieldNotUpdatableError,
  InvalidValidityPeriodError,
} from "../lib/errors.generated";
import { baseProfitCenter, activeProfitCenter, inactiveProfitCenter } from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./updateProfitCenter";

describe("updateProfitCenter", () => {
  it("updates name on a DRAFT profit center", async () => {
    const updated = { ...baseProfitCenter, name: "New Name" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseProfitCenter);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { profitCenterId: baseProfitCenter.id, name: "New Name" },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.profitCenter.name).toBe("New Name");
  });

  it("updates description on a DRAFT profit center", async () => {
    const updated = { ...baseProfitCenter, description: "Updated desc" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseProfitCenter);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { profitCenterId: baseProfitCenter.id, description: "Updated desc" },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.profitCenter.description).toBe("Updated desc");
  });

  it("updates description on an ACTIVE profit center", async () => {
    const updated = { ...activeProfitCenter, description: "Active desc" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(activeProfitCenter);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { profitCenterId: activeProfitCenter.id, description: "Active desc" },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.profitCenter.description).toBe("Active desc");
  });

  it("updates responsible person on a DRAFT profit center", async () => {
    const updated = { ...baseProfitCenter, responsiblePersonId: "user-2" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseProfitCenter);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { profitCenterId: baseProfitCenter.id, responsiblePersonId: "user-2" },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.profitCenter.responsiblePersonId).toBe("user-2");
  });

  it("updates responsible person on an ACTIVE profit center", async () => {
    const updated = { ...activeProfitCenter, responsiblePersonId: "user-2" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(activeProfitCenter);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { profitCenterId: activeProfitCenter.id, responsiblePersonId: "user-2" },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.profitCenter.responsiblePersonId).toBe("user-2");
  });

  it("returns error when changing restricted fields on an ACTIVE profit center", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(activeProfitCenter);

    const result = await run(
      db,
      { profitCenterId: activeProfitCenter.id, name: "New Name" },
      commandCtx,
    );

    expectErr(result, FieldNotUpdatableError);
  });

  it("returns error when profit center does not exist", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { profitCenterId: "missing-id" }, commandCtx);

    expectErr(result, ProfitCenterNotFoundError);
  });

  it("returns error when profit center is INACTIVE", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(inactiveProfitCenter);

    const result = await run(
      db,
      { profitCenterId: inactiveProfitCenter.id, description: "test" },
      commandCtx,
    );

    expectErr(result, InvalidStateError);
  });

  it("returns error when validTo is before validFrom", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseProfitCenter);

    const result = await run(
      db,
      {
        profitCenterId: baseProfitCenter.id,
        validFrom: new Date("2024-06-01"),
        validTo: new Date("2024-01-01"),
      },
      commandCtx,
    );

    expectErr(result, InvalidValidityPeriodError);
  });

  it("emits audit event recording previous and new field values", async () => {
    const updated = { ...baseProfitCenter, description: "New" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseProfitCenter);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { profitCenterId: baseProfitCenter.id, description: "New" },
      commandCtx,
    );

    expectOk(result);
    expect(spies.update).toHaveBeenCalled();
  });
});
