import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, it, expect } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  FiscalYearNotFoundError,
  NameRequiredError,
  DatesImmutableError,
  InvalidDateRangeError,
  OverlappingFiscalYearError,
} from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { baseFiscalYear, baseFiscalYear2, baseAccountingPeriod } from "../testing/fixtures";
import { run } from "./updateFiscalYear";

describe("updateFiscalYear", () => {
  it("returns error when fiscal year does not exist", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    expectErr(
      await mock.withTx((trx) =>
        run(trx, { id: "missing-id", yearName: "FY Updated" }, commandCtx),
      ),
      FiscalYearNotFoundError,
    );
  });

  it("returns error when name is empty", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseFiscalYear]);

    expectErr(
      await mock.withTx((trx) => run(trx, { id: baseFiscalYear.id, yearName: "" }, commandCtx)),
      NameRequiredError,
    );
  });

  it("returns error when attempting to change dates after accounting periods exist", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseFiscalYear], [baseAccountingPeriod]);

    expectErr(
      await mock.withTx((trx) =>
        run(trx, { id: baseFiscalYear.id, startDate: new Date("2024-02-01") }, commandCtx),
      ),
      DatesImmutableError,
    );
  });

  it("returns error when updated start date is not before end date", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseFiscalYear], []);

    expectErr(
      await mock.withTx((trx) =>
        run(
          trx,
          {
            id: baseFiscalYear.id,
            startDate: new Date("2025-01-01"),
            endDate: new Date("2024-06-01"),
          },
          commandCtx,
        ),
      ),
      InvalidDateRangeError,
    );
  });

  it("returns error when updated date range overlaps with another fiscal year", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseFiscalYear], [], [baseFiscalYear2]);

    expectErr(
      await mock.withTx((trx) =>
        run(
          trx,
          {
            id: baseFiscalYear.id,
            startDate: new Date("2024-06-01"),
            endDate: new Date("2025-06-01"),
          },
          commandCtx,
        ),
      ),
      OverlappingFiscalYearError,
    );
  });

  it("updates fiscal year name successfully", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const updated = { ...baseFiscalYear, name: "FY 2024 Renamed" };
    mock.enqueueResults([baseFiscalYear], [updated]);

    const result = await mock.withTx((trx) =>
      run(trx, { id: baseFiscalYear.id, yearName: "FY 2024 Renamed" }, commandCtx),
    );

    const value = expectOk(result);
    expect(value.fiscalYear).toEqual(updated);
  });

  it("updates fiscal year dates when no accounting periods exist", async () => {
    const newStart = new Date("2024-02-01");
    const newEnd = new Date("2024-11-30");
    const updated = { ...baseFiscalYear, startDate: newStart, endDate: newEnd };
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseFiscalYear], [], [], [updated]);

    const result = await mock.withTx((trx) =>
      run(trx, { id: baseFiscalYear.id, startDate: newStart, endDate: newEnd }, commandCtx),
    );

    const value = expectOk(result);
    expect(value.fiscalYear).toEqual(updated);
  });

  it("updates fiscal year name successfully", async () => {
    const updated = { ...baseFiscalYear, name: "FY 2024 Updated" };
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseFiscalYear], [updated]);

    const result = await mock.withTx((trx) =>
      run(trx, { id: baseFiscalYear.id, yearName: "FY 2024 Updated" }, commandCtx),
    );
    const value = expectOk(result);
    expect(value.fiscalYear).toEqual(updated);
  });
});
