import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  InternalOrderNotFoundError,
  InvalidStateError,
  OrderTypeChangeNotAllowedError,
  CostCenterNotFoundError,
  InvalidValidityPeriodError,
} from "../lib/errors.generated";
import {
  baseInternalOrder,
  releasedInternalOrder,
  techCompletedInternalOrder,
  closedInternalOrder,
  baseCostCenter2,
} from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./updateInternalOrder";

describe("updateInternalOrder", () => {
  it("updates description on a CREATED internal order", async () => {
    const updated = { ...baseInternalOrder, description: "New Description" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseInternalOrder);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { internalOrderId: baseInternalOrder.id, description: "New Description" },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.internalOrder.description).toBe("New Description");
  });

  it("updates description on a RELEASED internal order", async () => {
    const updated = { ...releasedInternalOrder, description: "Updated Desc" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(releasedInternalOrder);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { internalOrderId: releasedInternalOrder.id, description: "Updated Desc" },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.internalOrder.description).toBe("Updated Desc");
  });

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

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

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

  it("updates cost center on a CREATED internal order", async () => {
    const updated = { ...baseInternalOrder, costCenterId: baseCostCenter2.id };
    const { db, spies } = createMockDb<Transaction>();
    // order lookup, cost center lookup
    spies.select.mockReturnValueOnce(baseInternalOrder).mockReturnValueOnce(baseCostCenter2);
    spies.update.mockReturnValueOnce(updated);

    const result = await run(
      db,
      { internalOrderId: baseInternalOrder.id, costCenterId: baseCostCenter2.id },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.internalOrder.costCenterId).toBe(baseCostCenter2.id);
  });

  it("updates validity dates on a CREATED internal order", async () => {
    const updated = {
      ...baseInternalOrder,
      validFrom: new Date("2024-06-01"),
      validTo: new Date("2024-12-31"),
    };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseInternalOrder);
    spies.update.mockReturnValueOnce(updated);

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

    const value = expectOk(result);
    expect(value.internalOrder.validFrom).toEqual(new Date("2024-06-01"));
    expect(value.internalOrder.validTo).toEqual(new Date("2024-12-31"));
  });

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

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

    expectErr(result, InternalOrderNotFoundError);
  });

  it("returns error when internal order is TECHNICALLY_COMPLETED", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(techCompletedInternalOrder);

    const result = await run(
      db,
      { internalOrderId: techCompletedInternalOrder.id, description: "Update" },
      commandCtx,
    );

    expectErr(result, InvalidStateError);
  });

  it("returns error when internal order is CLOSED", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(closedInternalOrder);

    const result = await run(
      db,
      { internalOrderId: closedInternalOrder.id, description: "Update" },
      commandCtx,
    );

    expectErr(result, InvalidStateError);
  });

  it("returns error when changing order type after release", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(releasedInternalOrder);

    const result = await run(
      db,
      { internalOrderId: releasedInternalOrder.id, orderType: "INVESTMENT" },
      commandCtx,
    );

    expectErr(result, OrderTypeChangeNotAllowedError);
  });

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

    const result = await run(
      db,
      { internalOrderId: baseInternalOrder.id, costCenterId: "missing-cc" },
      commandCtx,
    );

    expectErr(result, CostCenterNotFoundError);
  });

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

    const result = await run(
      db,
      {
        internalOrderId: baseInternalOrder.id,
        validFrom: new Date("2025-12-31"),
        validTo: new Date("2025-01-01"),
      },
      commandCtx,
    );

    expectErr(result, InvalidValidityPeriodError);
  });

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

    const result = await run(
      db,
      { internalOrderId: baseInternalOrder.id, description: "Audit Test" },
      commandCtx,
    );

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