import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  CompanyNotFoundError,
  NameRequiredError,
  InvalidDateRangeError,
  OverlappingFiscalYearError,
} from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { baseCompany, baseFiscalYear } from "../testing/fixtures";
import { mockOrgQueries } from "../testing/moduleMocks";
import { run } from "./createFiscalYear";

describe("createFiscalYear", () => {
  it("returns error when company does not exist", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: "missing-company",
          name: "FY 2024",
          startDate: new Date("2024-01-01"),
          endDate: new Date("2024-12-31"),
        },
        commandCtx,
        mockOrgQueries(null),
      ),
    );

    expectErr(result, CompanyNotFoundError);
  });

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

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: baseCompany.id,
          name: "",
          startDate: new Date("2024-01-01"),
          endDate: new Date("2024-12-31"),
        },
        commandCtx,
        mockOrgQueries(baseCompany),
      ),
    );

    expectErr(result, NameRequiredError);
  });

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

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: baseCompany.id,
          name: "FY 2024",
          startDate: new Date("2024-12-31"),
          endDate: new Date("2024-01-01"),
        },
        commandCtx,
        mockOrgQueries(baseCompany),
      ),
    );

    expectErr(result, InvalidDateRangeError);
  });

  it("returns error when fiscal year overlaps with an existing fiscal year for the same company", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseFiscalYear]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: baseCompany.id,
          name: "FY 2024 Overlap",
          startDate: new Date("2024-06-01"),
          endDate: new Date("2025-05-31"),
        },
        commandCtx,
        mockOrgQueries(baseCompany),
      ),
    );

    expectErr(result, OverlappingFiscalYearError);
  });

  it("creates fiscal year with valid name, start date, and end date", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([], [baseFiscalYear]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: baseCompany.id,
          name: "FY 2024",
          startDate: new Date("2024-01-01"),
          endDate: new Date("2024-12-31"),
        },
        commandCtx,
        mockOrgQueries(baseCompany),
      ),
    );

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

  it("creates non-calendar fiscal year (e.g., April-to-March) with correct date boundaries", async () => {
    const nonCalendarFiscalYear = {
      ...baseFiscalYear,
      id: "fiscal-year-non-calendar",
      name: "FY 2024-2025",
      startDate: new Date("2024-04-01"),
      endDate: new Date("2025-03-31"),
    };
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([], [nonCalendarFiscalYear]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: baseCompany.id,
          name: "FY 2024-2025",
          startDate: new Date("2024-04-01"),
          endDate: new Date("2025-03-31"),
        },
        commandCtx,
        mockOrgQueries(baseCompany),
      ),
    );

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

  it("fiscal years from different companies are isolated", async () => {
    const companyB = { ...baseCompany, id: "company-2" };
    const fiscalYearCompanyB = {
      ...baseFiscalYear,
      id: "fiscal-year-company-b",
      companyId: companyB.id,
    };
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([], [fiscalYearCompanyB]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: companyB.id,
          name: "FY 2024",
          startDate: new Date("2024-01-01"),
          endDate: new Date("2024-12-31"),
        },
        commandCtx,
        mockOrgQueries(companyB),
      ),
    );

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