import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  InvalidCodeError,
  InvalidNameError,
  CompanyNotFoundError,
  DuplicateCodeError,
  ParentNotFoundError,
} from "../lib/errors.generated";
import { activeCompany, activeDepartment, otherCompanyDepartment } from "../testing/fixtures";
import { run } from "./createDepartment";

describe("createDepartment", () => {
  it("creates top-level department in ACTIVE status", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const created = {
      id: "new-dept-id",
      code: "HR",
      name: "Human Resources",
      companyId: activeCompany.id,
      parentDepartmentId: null,
      status: "ACTIVE",
      createdAt: new Date(),
      updatedAt: null,
    };

    mock.enqueueResults(
      [activeCompany], // find company
      [], // no duplicate code
      [created],
    );

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "HR",
        name: "Human Resources",
        companyId: activeCompany.id,
      }),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.department.status).toBe("ACTIVE");
      expect(result.value.department.parentDepartmentId).toBeNull();
    }
  });

  it("creates department with parent department", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const created = {
      id: "new-dept-id",
      code: "ENG-FE",
      name: "Frontend Engineering",
      companyId: activeCompany.id,
      parentDepartmentId: activeDepartment.id,
      status: "ACTIVE",
      createdAt: new Date(),
      updatedAt: null,
    };

    mock.enqueueResults(
      [activeCompany], // find company
      [], // no duplicate code
      [activeDepartment], // find parent
      [created],
    );

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "ENG-FE",
        name: "Frontend Engineering",
        companyId: activeCompany.id,
        parentDepartmentId: activeDepartment.id,
      }),
    );

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.department.parentDepartmentId).toBe(activeDepartment.id);
    }
  });

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

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "",
        name: "Human Resources",
        companyId: activeCompany.id,
      }),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(InvalidCodeError);
    }
  });

  it("throws when code is whitespace-only", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "   ",
        name: "Human Resources",
        companyId: activeCompany.id,
      }),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(InvalidCodeError);
    }
  });

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

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "HR",
        name: "",
        companyId: activeCompany.id,
      }),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(InvalidNameError);
    }
  });

  it("throws when name is whitespace-only", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "HR",
        name: "   ",
        companyId: activeCompany.id,
      }),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(InvalidNameError);
    }
  });

  it("throws when company does not exist", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]); // company not found

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "HR",
        name: "Human Resources",
        companyId: "nonexistent-company",
      }),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(CompanyNotFoundError);
    }
  });

  it("throws when code already exists in same company", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [activeCompany], // find company
      [activeDepartment], // duplicate found
    );

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: activeDepartment.code,
        name: "Duplicate Department",
        companyId: activeCompany.id,
      }),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(DuplicateCodeError);
    }
  });

  it("allows same code in different companies", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const otherCompany = { ...activeCompany, id: "company-3" };
    const created = {
      id: "new-dept-id",
      code: "ENG",
      name: "Engineering",
      companyId: otherCompany.id,
      parentDepartmentId: null,
      status: "ACTIVE",
      createdAt: new Date(),
      updatedAt: null,
    };

    mock.enqueueResults(
      [otherCompany], // find company
      [], // no duplicate in this company
      [created],
    );

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "ENG",
        name: "Engineering",
        companyId: otherCompany.id,
      }),
    );

    expect(result.ok).toBe(true);
  });

  it("throws when parent department does not exist", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [activeCompany], // find company
      [], // no duplicate code
      [], // parent not found
    );

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "HR-SUB",
        name: "HR Sub-team",
        companyId: activeCompany.id,
        parentDepartmentId: "nonexistent-dept",
      }),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(ParentNotFoundError);
    }
  });

  it("throws when parent department belongs to different company", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults(
      [activeCompany], // find company
      [], // no duplicate code
      [otherCompanyDepartment], // parent in different company
    );

    const result = await mock.withTx((trx) =>
      run(trx, {
        code: "HR-SUB",
        name: "HR Sub-team",
        companyId: activeCompany.id,
        parentDepartmentId: otherCompanyDepartment.id,
      }),
    );

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(ParentNotFoundError);
    }
  });
});
