import { describe, expect, it } from "vitest";
import { moduleNameToPrefix, generateErrors } from "./generate-errors";
import { generatePermissions } from "./generate-permissions";
import { generateCommandShell, generateQueryShell } from "./generate-shells";
import { renderStub } from "./stub-templates";
import type { ParsedCommandDoc } from "./parse-command-doc";

describe("moduleNameToPrefix", () => {
  it("converts module name to UPPER_SNAKE_CASE prefix", () => {
    expect(moduleNameToPrefix("item-management")).toBe("ITEM_MANAGEMENT");
    expect(moduleNameToPrefix("primitives")).toBe("PRIMITIVES");
    expect(moduleNameToPrefix("user-management")).toBe("USER_MANAGEMENT");
    expect(moduleNameToPrefix("product-management")).toBe("PRODUCT_MANAGEMENT");
  });
});

describe("generateErrors", () => {
  it("generates error classes with module-prefixed codes", () => {
    const docs: ParsedCommandDoc[] = [
      {
        commandName: "createItem",
        errors: [
          { code: "DUPLICATE_SKU", description: "An item with the same SKU already exists" },
          { code: "UNIT_NOT_FOUND", description: "Referenced unit does not exist or is inactive" },
        ],
        externalDependencies: [],
      },
      {
        commandName: "activateItem",
        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 = generateErrors("item-management", docs);

    expect(result).toContain("// @generated — do not edit");
    expect(result).toContain(
      'import { createDomainError } from "@tailor-platform/erp-kit/module";',
    );
    expect(result).toContain("export const DuplicateSkuError = createDomainError(");
    expect(result).toContain('"DuplicateSkuError", "ITEM_MANAGEMENT_DUPLICATE_SKU"');
    expect(result).toContain("export const UnitNotFoundError = createDomainError(");
    expect(result).toContain('"UnitNotFoundError", "ITEM_MANAGEMENT_UNIT_NOT_FOUND"');
    expect(result).toContain("export const ItemNotFoundError = createDomainError(");
    expect(result).toContain("export const InvalidStateTransitionError = createDomainError(");
  });

  it("deduplicates error codes across commands", () => {
    const docs: ParsedCommandDoc[] = [
      {
        commandName: "createItem",
        errors: [{ code: "ITEM_NOT_FOUND", description: "Item does not exist" }],
        externalDependencies: [],
      },
      {
        commandName: "updateItem",
        errors: [{ code: "ITEM_NOT_FOUND", description: "Item does not exist" }],
        externalDependencies: [],
      },
    ];

    const result = generateErrors("item-management", docs);

    const matches = result.match(/ItemNotFoundError/g);
    // Class name appears exactly twice: export declaration + first arg to createDomainError
    expect(matches).toHaveLength(2);
  });

  it("returns empty string when no errors", () => {
    const docs: ParsedCommandDoc[] = [
      { commandName: "noop", errors: [], externalDependencies: [] },
    ];

    const result = generateErrors("item-management", docs);

    expect(result).toBe("");
  });
});

describe("generatePermissions", () => {
  it("generates permissions from command names", () => {
    const commandNames = ["createItem", "updateItem", "activateItem", "deleteItem"];

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

    expect(result).toContain("// @generated — do not edit");
    expect(result).toContain(
      'import { definePermissions } from "@tailor-platform/erp-kit/module";',
    );
    expect(result).toContain('"item-management"');
    expect(result).toContain('"createItem"');
    expect(result).toContain('"updateItem"');
    expect(result).toContain('"activateItem"');
    expect(result).toContain('"deleteItem"');
    expect(result).toContain("as const");
  });

  it("sorts command names alphabetically", () => {
    const commandNames = ["deleteItem", "activateItem", "createItem"];

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

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

describe("generateCommandShell", () => {
  it("generates defineCommand wiring that imports run from hand-written file", () => {
    const doc: ParsedCommandDoc = {
      commandName: "activateItem",
      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/module";');
    expect(result).toContain('import { permissions } from "../lib/permissions.generated";');
    expect(result).toContain('import { run } from "./activateItem";');
    expect(result).toContain(
      "export const activateItem = defineCommand(permissions.activateItem, run);",
    );
  });

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

    const result = generateCommandShell(doc);

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

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

    const result = generateQueryShell(doc);

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

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

    expect(result).toContain('import type { ReadonlyDB } from "@tailor-platform/erp-kit/module";');
    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("renderStub db", () => {
  it("generates an empty db type definition with TODO", () => {
    const result = renderStub("db", "item");

    expect(result).toContain(
      "import { db, unsafeAllowAllGqlPermission, unsafeAllowAllTypePermission }",
    );
    expect(result).toContain("export const item = db");
    expect(result).toContain('.type("Item"');
    expect(result).toContain("// TODO: define fields");
    expect(result).toContain("...db.fields.timestamps()");
    expect(result).toContain(".permission(unsafeAllowAllTypePermission)");
    expect(result).toContain(".gqlPermission(unsafeAllowAllGqlPermission)");
  });

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

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

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

    expect(result).toContain('import { describe, expect, it } from "vitest";');
    expect(result).toContain('import { createMockDb } from "../../../testing/index";');
    expect(result).toContain('import type { DB } from "../generated/kysely-tailordb";');
    expect(result).toContain('import { run, type GetTaxonomyNodeInput } from "./getTaxonomyNode";');
    expect(result).toContain('describe("getTaxonomyNode"');
    expect(result).toContain("const { db } = createMockDb<DB>();");
    expect(result).toContain("await run(db, {} as GetTaxonomyNodeInput)");
    expect(result).toContain("expect(result).toBeDefined();");
  });
});

describe("renderStub resolver", () => {
  it("generates a createResolver stub with SDK imports and transaction boilerplate", () => {
    const result = renderStub("resolver", "createItem");

    expect(result).toContain('import { createResolver, t } from "@tailor-platform/sdk";');
    expect(result).toContain('import { createContext } from "@tailor-platform/erp-kit/app";');
    expect(result).toContain('import { getDB } from "@/generated/kysely-tailordb";');
    expect(result).toContain('name: "createItem"');
    expect(result).toContain('operation: "mutation"');
    expect(result).toContain('getDB("main-db")');
    expect(result).toContain("db.transaction().execute");
    expect(result).toContain('.description("CreateItem response")');
  });
});
