import { describe, expect, it } from "vitest";
import { generatePermissions } from "./generate-permissions";
import { generateCommandShell, generateQueryShell } from "./generate-shells";
import type { ParsedCommandDoc } from "./parse-command-doc";
import { renderDbStub, renderQueryStub, renderQueryTestStub } from "./stubs";

describe("generatePermissions", () => {
  it("generates scoped permissions grouped by permissionScope", () => {
    const commands = [
      { name: "createItem", permissionScope: "item" },
      { name: "updateItem", permissionScope: "item" },
      { name: "activateItem", permissionScope: "item" },
      { name: "createTaxonomyNode", permissionScope: "taxonomy" },
    ];

    const result = generatePermissions("item-management", commands);

    expect(result).toContain("// @generated — do not edit");
    expect(result).toContain('import { definePermissions } from "@tailor-platform/erp-kit/core";');
    expect(result).toContain('"item-management"');
    expect(result).toContain("item: [");
    expect(result).toContain('"createItem"');
    expect(result).toContain("taxonomy: [");
    expect(result).toContain('"createTaxonomyNode"');
    expect(result).toContain("as const");
  });

  it("sorts scopes and commands alphabetically", () => {
    const commands = [
      { name: "deleteItem", permissionScope: "item" },
      { name: "createTaxonomyNode", permissionScope: "taxonomy" },
      { name: "activateItem", permissionScope: "item" },
    ];

    const result = generatePermissions("item-management", commands);

    const itemIdx = result.indexOf("item: [");
    const taxonomyIdx = result.indexOf("taxonomy: [");
    expect(itemIdx).toBeLessThan(taxonomyIdx);

    const activateIdx = result.indexOf('"activateItem"');
    const deleteIdx = result.indexOf('"deleteItem"');
    expect(activateIdx).toBeLessThan(deleteIdx);
  });

  it("throws when permissionScope is missing", () => {
    const commands = [{ name: "createItem", permissionScope: "" }];

    expect(() => generatePermissions("item-management", commands)).toThrow(
      "missing a Permission Scope",
    );
  });
});

describe("generateCommandShell", () => {
  it("generates defineCommand wiring that imports run from hand-written file", () => {
    const doc: ParsedCommandDoc = {
      commandName: "activateItem",
      permissionScope: "item",
      errors: [
        { code: "ITEM_NOT_FOUND", description: "Item does not exist" },
        { code: "INVALID_STATE_TRANSITION", description: "Item is not in a valid state" },
      ],
      externalDependencies: [],
    };

    const result = generateCommandShell(doc);

    expect(result).toContain("// @generated — do not edit");
    expect(result).toContain('import { defineCommand } from "@tailor-platform/erp-kit/core";');
    expect(result).toContain('import { permissions } from "../lib/permissions.generated";');
    expect(result).toContain('import { run } from "./activateItem";');
    expect(result).toContain(
      'export const activateItem = defineCommand("activateItem", permissions.item.activateItem, run);',
    );
  });

  it("generates defineCommand for commands with external dependencies", () => {
    const doc: ParsedCommandDoc = {
      commandName: "createItem",
      permissionScope: "item",
      errors: [],
      externalDependencies: [{ module: "primitives", entity: "Unit" }],
    };

    const result = generateCommandShell(doc);

    expect(result).toContain('import { defineCommand } from "@tailor-platform/erp-kit/core";');
    expect(result).toContain('import { run } from "./createItem";');
    expect(result).toContain(
      'export const createItem = defineCommand("createItem", permissions.item.createItem, run);',
    );
  });
});

describe("generateQueryShell", () => {
  it("generates defineQuery wiring that imports run from hand-written file", () => {
    const doc: ParsedCommandDoc = {
      commandName: "getTaxonomyNode",
      permissionScope: "taxonomy",
      errors: [],
      externalDependencies: [],
    };

    const result = generateQueryShell(doc);

    expect(result).toContain("// @generated — do not edit");
    expect(result).toContain('import { defineQuery } from "@tailor-platform/erp-kit/core";');
    expect(result).toContain('import { run } from "./getTaxonomyNode";');
    expect(result).toContain('export const getTaxonomyNode = defineQuery("getTaxonomyNode", run);');
  });
});

describe("renderQueryStub", () => {
  it("generates a stub implementation with ReadonlyDB and input interface", () => {
    const result = renderQueryStub("getTaxonomyNode");

    expect(result).toContain('import type { ReadonlyDB } from "@tailor-platform/erp-kit/core";');
    expect(result).toContain('import type { DB } from "../generated/kysely-tailordb";');
    expect(result).toContain("export interface GetTaxonomyNodeInput {");
    expect(result).toContain(
      "export async function run(db: ReadonlyDB<DB>, input: GetTaxonomyNodeInput)",
    );
    expect(result).toContain("return {};");
  });
});

describe("renderDbStub", () => {
  it("generates a guarded factory db type definition with TODO", () => {
    const result = renderDbStub("item");

    expect(result).toContain('from "@tailor-platform/erp-kit/core"');
    expect(result).toContain("// TODO: define fields");
    expect(result).toContain("fields?: NoReservedFields<F, ReservedFields>");
    expect(result).toContain("export function createItemType");
    expect(result).toContain("...db.fields.timestamps()");
    expect(result).toContain(".permission(defaultPermission)");
  });

  it("handles camelCase model names", () => {
    const result = renderDbStub("taxonomyNode");

    expect(result).toContain("export function createTaxonomyNodeType");
    expect(result).toContain('.type("TaxonomyNode"');
    expect(result).toContain("export const taxonomyNode = createTaxonomyNodeType({});");
  });
});

describe("renderQueryTestStub", () => {
  it("generates a test stub with createKyselyMock and run import", () => {
    const result = renderQueryTestStub("getTaxonomyNode");

    expect(result).toContain('import { createKyselyMock } from "@tailor-platform/sdk/vitest";');
    expect(result).toContain('import { describe, expect, it } from "vitest";');
    expect(result).toContain('import type { Namespace } from "../generated/kysely-tailordb";');
    expect(result).toContain('import { run, type GetTaxonomyNodeInput } from "./getTaxonomyNode";');
    expect(result).toContain('describe("getTaxonomyNode"');
    expect(result).toContain("const mock = createKyselyMock<Namespace[keyof Namespace]>();");
    expect(result).toContain("await run(mock.db, {} as GetTaxonomyNodeInput)");
    expect(result).toContain("expect(result).toBeDefined();");
  });
});
