import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  CompanyNotFoundError,
  DuplicateProfitCenterCodeError,
  InvalidNameError,
  ValidFromRequiredError,
  InvalidValidityPeriodError,
} from "../lib/errors.generated";
import { baseCompany, baseProfitCenter } from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./createProfitCenter";

describe("createProfitCenter", () => {
  it("creates profit center in DRAFT status with required fields", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseCompany).mockReturnValueOnce(undefined);
    spies.insert.mockReturnValueOnce(baseProfitCenter);

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-001",
        name: "Product Line Alpha",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.profitCenter).toEqual(baseProfitCenter);
  });

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

    const result = await run(
      db,
      {
        companyId: "missing-company",
        code: "PC-001",
        name: "Test",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

    expectErr(result, CompanyNotFoundError);
  });

  it("returns error when company is not ACTIVE", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({ ...baseCompany, status: "INACTIVE" });

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-001",
        name: "Test",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

    expectErr(result, CompanyNotFoundError);
  });

  it("returns error when code already exists within the company", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseCompany).mockReturnValueOnce(baseProfitCenter);

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-001",
        name: "Test",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

    expectErr(result, DuplicateProfitCenterCodeError);
  });

  it("returns error when name is missing", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseCompany).mockReturnValueOnce(undefined);

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-002",
        name: "",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

    expectErr(result, InvalidNameError);
  });

  it("returns error when validFrom is not provided", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseCompany).mockReturnValueOnce(undefined);

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-002",
        name: "Test",
        validFrom: null,
      },
      commandCtx,
    );

    expectErr(result, ValidFromRequiredError);
  });

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

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-002",
        name: "Test",
        validFrom: new Date("2024-06-01"),
        validTo: new Date("2024-01-01"),
      },
      commandCtx,
    );

    expectErr(result, InvalidValidityPeriodError);
  });

  it("creates profit center with optional description", async () => {
    const pcWithDesc = { ...baseProfitCenter, description: "A description" };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseCompany).mockReturnValueOnce(undefined);
    spies.insert.mockReturnValueOnce(pcWithDesc);

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-001",
        name: "Product Line Alpha",
        description: "A description",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

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

  it("creates profit center without validTo", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseCompany).mockReturnValueOnce(undefined);
    spies.insert.mockReturnValueOnce(baseProfitCenter);

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-001",
        name: "Product Line Alpha",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

    expectOk(result);
  });

  it("profit centers from different companies are isolated", async () => {
    const companyB = { id: "company-2", status: "ACTIVE" };
    const pcCompanyB = { ...baseProfitCenter, id: "pc-b", companyId: companyB.id };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(companyB).mockReturnValueOnce(undefined);
    spies.insert.mockReturnValueOnce(pcCompanyB);

    const result = await run(
      db,
      {
        companyId: companyB.id,
        code: "PC-001",
        name: "Product Line Alpha",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.profitCenter.companyId).toBe(companyB.id);
  });

  it("emits audit event recording acting user, timestamp, and initial field values", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseCompany).mockReturnValueOnce(undefined);
    spies.insert.mockReturnValueOnce(baseProfitCenter);

    const result = await run(
      db,
      {
        companyId: baseCompany.id,
        code: "PC-001",
        name: "Product Line Alpha",
        validFrom: new Date("2024-01-01"),
      },
      commandCtx,
    );

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