import { z } from "zod";
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { loadCustomFieldValues } from "@open-mercato/shared/lib/crud/custom-fields";
import { E } from "../../../generated/entities.ids.generated.js";
import { CatalogProductCategory } from "../data/entities.js";
import { assertTenantScope } from "./types.js";
function resolveEm(ctx) {
  return ctx.container.resolve("em");
}
function buildScope(ctx, tenantId) {
  return { tenantId, organizationId: ctx.organizationId };
}
const listCategoriesInput = z.object({
  parentId: z.union([z.string().uuid(), z.null()]).optional().describe("Parent category id; pass `null` to list root nodes. Omit to list every category in scope."),
  limit: z.number().int().min(1).max(100).optional().describe("Max rows (default 50, max 100)."),
  offset: z.number().int().min(0).optional().describe("Rows to skip (default 0)."),
  includeArchived: z.boolean().optional().describe("When true, include soft-deleted categories. Defaults to active-only.")
}).passthrough();
const listCategoriesTool = {
  name: "catalog.list_categories",
  displayName: "List categories",
  description: "List catalog categories scoped to tenant + organization. Use `parentId: null` to list roots or a specific uuid to fetch direct children.",
  inputSchema: listCategoriesInput,
  requiredFeatures: ["catalog.categories.view"],
  tags: ["read", "catalog"],
  handler: async (rawInput, ctx) => {
    const { tenantId } = assertTenantScope(ctx);
    const input = listCategoriesInput.parse(rawInput);
    const em = resolveEm(ctx);
    const limit = input.limit ?? 50;
    const offset = input.offset ?? 0;
    const where = { tenantId };
    if (ctx.organizationId) where.organizationId = ctx.organizationId;
    if (!input.includeArchived) where.deletedAt = null;
    if ("parentId" in input) {
      where.parentId = input.parentId ?? null;
    }
    const [rows, total] = await Promise.all([
      findWithDecryption(
        em,
        CatalogProductCategory,
        where,
        { limit, offset, orderBy: { depth: "asc", name: "asc" } },
        buildScope(ctx, tenantId)
      ),
      em.count(CatalogProductCategory, where)
    ]);
    const filtered = rows.filter((row) => row.tenantId === tenantId);
    return {
      items: filtered.map((row) => ({
        id: row.id,
        name: row.name,
        slug: row.slug ?? null,
        description: row.description ?? null,
        parentId: row.parentId ?? null,
        rootId: row.rootId ?? null,
        treePath: row.treePath ?? null,
        depth: row.depth,
        childIds: Array.isArray(row.childIds) ? row.childIds : [],
        isActive: !!row.isActive,
        organizationId: row.organizationId ?? null,
        tenantId: row.tenantId ?? null,
        createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null
      })),
      total,
      limit,
      offset
    };
  }
};
const getCategoryInput = z.object({
  categoryId: z.string().uuid().describe("Category id (UUID)."),
  includeRelated: z.boolean().optional().describe(
    "When true, include direct children (capped at 100) and inherited ancestor refs. Custom fields are always included."
  )
});
const getCategoryTool = {
  name: "catalog.get_category",
  displayName: "Get category",
  description: "Fetch a catalog category by id with core fields and (optionally) children + ancestor inheritance + custom fields. Returns { found: false } when missing or cross-tenant.",
  inputSchema: getCategoryInput,
  requiredFeatures: ["catalog.categories.view"],
  tags: ["read", "catalog"],
  handler: async (rawInput, ctx) => {
    const { tenantId } = assertTenantScope(ctx);
    const input = getCategoryInput.parse(rawInput);
    const em = resolveEm(ctx);
    const where = {
      id: input.categoryId,
      tenantId,
      deletedAt: null
    };
    if (ctx.organizationId) where.organizationId = ctx.organizationId;
    const category = await findOneWithDecryption(
      em,
      CatalogProductCategory,
      where,
      void 0,
      buildScope(ctx, tenantId)
    );
    if (!category || category.tenantId !== tenantId) {
      return { found: false, categoryId: input.categoryId };
    }
    const customFieldValues = await loadCustomFieldValues({
      em,
      entityId: E.catalog.catalog_product_category,
      recordIds: [category.id],
      tenantIdByRecord: { [category.id]: category.tenantId ?? null },
      organizationIdByRecord: { [category.id]: category.organizationId ?? null },
      tenantFallbacks: [category.tenantId ?? tenantId].filter((value) => !!value)
    });
    const customFields = customFieldValues[category.id] ?? {};
    let related = null;
    if (input.includeRelated) {
      const scope = buildScope(ctx, tenantId);
      const children = await findWithDecryption(
        em,
        CatalogProductCategory,
        { tenantId, parentId: category.id, deletedAt: null },
        { limit: 100, orderBy: { name: "asc" } },
        scope
      );
      related = {
        children: children.filter((row) => row.tenantId === tenantId).map((row) => ({
          id: row.id,
          name: row.name,
          slug: row.slug ?? null,
          depth: row.depth,
          isActive: !!row.isActive
        })),
        ancestorIds: Array.isArray(category.ancestorIds) ? [...category.ancestorIds] : [],
        descendantIds: Array.isArray(category.descendantIds) ? [...category.descendantIds] : []
      };
    }
    return {
      found: true,
      category: {
        id: category.id,
        name: category.name,
        slug: category.slug ?? null,
        description: category.description ?? null,
        parentId: category.parentId ?? null,
        rootId: category.rootId ?? null,
        treePath: category.treePath ?? null,
        depth: category.depth,
        childIds: Array.isArray(category.childIds) ? [...category.childIds] : [],
        ancestorIds: Array.isArray(category.ancestorIds) ? [...category.ancestorIds] : [],
        descendantIds: Array.isArray(category.descendantIds) ? [...category.descendantIds] : [],
        metadata: category.metadata ?? null,
        isActive: !!category.isActive,
        organizationId: category.organizationId ?? null,
        tenantId: category.tenantId ?? null,
        createdAt: category.createdAt ? new Date(category.createdAt).toISOString() : null,
        updatedAt: category.updatedAt ? new Date(category.updatedAt).toISOString() : null
      },
      customFields,
      related
    };
  }
};
const categoriesAiTools = [listCategoriesTool, getCategoryTool];
var categories_pack_default = categoriesAiTools;
export {
  categoriesAiTools,
  categories_pack_default as default
};
//# sourceMappingURL=categories-pack.js.map
