import { z } from "zod";
import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries } from "@open-mercato/shared/lib/crud/custom-fields";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { CatalogProduct, CatalogProductVariant } from "../../data/entities.js";
import { variantCreateSchema, variantUpdateSchema } from "../../data/validators.js";
import { parseScopedCommandInput, resolveCrudRecordId } from "../utils.js";
import { E } from "../../../../generated/entities.ids.generated.js";
import * as FV from "../../../../generated/entities/catalog_product_variant/index.js";
import { parseBooleanFlag, sanitizeSearchTerm } from "../helpers.js";
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
import {
  createCatalogCrudOpenApi,
  createPagedListResponseSchema,
  defaultOkResponseSchema
} from "../openapi.js";
const rawBodySchema = z.object({}).passthrough();
const SENTINEL_ID_VALUES = /* @__PURE__ */ new Set(["", "create", "new", "null", "undefined"]);
function stripPlaceholderId(source) {
  if (!Object.prototype.hasOwnProperty.call(source, "id")) return;
  const value = source.id;
  if (value === void 0 || value === null) {
    delete source.id;
    return;
  }
  if (typeof value === "string") {
    const raw = value.trim();
    if (!raw || SENTINEL_ID_VALUES.has(raw.toLowerCase())) {
      delete source.id;
    } else {
      source.id = raw;
    }
  }
}
const listSchema = z.object({
  page: z.coerce.number().min(1).default(1),
  pageSize: z.coerce.number().min(1).max(100).default(50),
  id: z.string().uuid().optional(),
  search: z.string().optional(),
  productId: z.string().uuid().optional(),
  sku: z.string().optional(),
  isActive: z.string().optional(),
  isDefault: z.string().optional(),
  withDeleted: z.coerce.boolean().optional(),
  sortField: z.string().optional(),
  sortDir: z.enum(["asc", "desc"]).optional()
}).passthrough();
const routeMetadata = {
  GET: { requireAuth: true, requireFeatures: ["catalog.products.view"] },
  POST: { requireAuth: true, requireFeatures: ["catalog.variants.manage"] },
  PUT: { requireAuth: true, requireFeatures: ["catalog.variants.manage"] },
  DELETE: { requireAuth: true, requireFeatures: ["catalog.variants.manage"] }
};
const metadata = routeMetadata;
async function buildVariantFilters(query) {
  const filters = {};
  const term = sanitizeSearchTerm(query.search);
  if (query.id) {
    filters.id = { $eq: query.id };
  }
  if (term) {
    const like = `%${escapeLikePattern(term)}%`;
    filters.$or = [
      { name: { $ilike: like } },
      { sku: { $ilike: like } },
      { barcode: { $ilike: like } }
    ];
  }
  if (query.productId) {
    filters.product_id = { $eq: query.productId };
  }
  if (query.sku && query.sku.trim()) {
    filters.sku = { $eq: query.sku.trim() };
  }
  const isActive = parseBooleanFlag(query.isActive);
  if (isActive !== void 0) filters.is_active = isActive;
  const isDefault = parseBooleanFlag(query.isDefault);
  if (isDefault !== void 0) filters.is_default = isDefault;
  return filters;
}
const crud = makeCrudRoute({
  metadata: routeMetadata,
  orm: {
    entity: CatalogProductVariant,
    idField: "id",
    orgField: "organizationId",
    tenantField: "tenantId",
    softDeleteField: "deletedAt"
  },
  indexer: { entityType: E.catalog.catalog_product_variant },
  list: {
    schema: listSchema,
    entityId: E.catalog.catalog_product_variant,
    fields: [
      FV.id,
      "product_id",
      FV.name,
      FV.sku,
      FV.barcode,
      "gtin_type",
      "hs_code",
      FV.status_entry_id,
      FV.is_default,
      FV.is_active,
      FV.weight_value,
      FV.weight_unit,
      "tax_rate_id",
      "tax_rate",
      FV.dimensions,
      FV.metadata,
      FV.option_values,
      "custom_fieldset_code",
      "default_media_id",
      "default_media_url",
      FV.created_at,
      FV.updated_at
    ],
    sortFieldMap: {
      name: FV.name,
      sku: FV.sku,
      createdAt: FV.created_at,
      updatedAt: FV.updated_at
    },
    buildFilters: async (query, ctx) => {
      const filters = await buildVariantFilters(query);
      const tenantId = ctx.auth?.tenantId ?? null;
      try {
        const em = ctx.container.resolve("em");
        const cfFilters = await buildCustomFieldFiltersFromQuery({
          entityIds: [E.catalog.catalog_product_variant],
          query,
          em,
          tenantId
        });
        Object.assign(filters, cfFilters);
      } catch {
      }
      return filters;
    },
    decorateCustomFields: { entityIds: [E.catalog.catalog_product_variant] },
    transformItem: (item) => {
      if (!item) return item;
      const normalized = { ...item };
      const cfEntries = extractAllCustomFieldEntries(item);
      for (const key of Object.keys(normalized)) {
        if (key.startsWith("cf:")) delete normalized[key];
      }
      return { ...normalized, ...cfEntries };
    }
  },
  actions: {
    create: {
      commandId: "catalog.variants.create",
      schema: rawBodySchema,
      mapInput: async ({ raw, ctx }) => {
        const { translate } = await resolveTranslations();
        const payload = raw && typeof raw === "object" ? { ...raw } : {};
        stripPlaceholderId(payload);
        const productId = typeof payload.productId === "string" ? payload.productId : null;
        if (productId) {
          try {
            const em = ctx.container.resolve("em");
            const product = await em.findOne(
              CatalogProduct,
              { id: productId, deletedAt: null },
              { fields: ["id", "organizationId", "tenantId"] }
            );
            if (product) {
              payload.organizationId = product.organizationId;
              payload.tenantId = product.tenantId;
            }
          } catch {
          }
        }
        return parseScopedCommandInput(variantCreateSchema, payload, ctx, translate);
      },
      response: ({ result }) => ({ id: result?.variantId ?? result?.id ?? null }),
      status: 201
    },
    update: {
      commandId: "catalog.variants.update",
      schema: rawBodySchema,
      mapInput: async ({ raw, ctx }) => {
        const { translate } = await resolveTranslations();
        const payload = raw && typeof raw === "object" ? { ...raw } : {};
        stripPlaceholderId(payload);
        const variantId = typeof payload.id === "string" ? payload.id : null;
        if (variantId) {
          try {
            const em = ctx.container.resolve("em");
            const variant = await em.findOne(
              CatalogProductVariant,
              { id: variantId, deletedAt: null },
              { fields: ["id", "organizationId", "tenantId"] }
            );
            if (variant) {
              payload.organizationId = variant.organizationId;
              payload.tenantId = variant.tenantId;
            }
          } catch {
          }
        }
        return parseScopedCommandInput(variantUpdateSchema, payload, ctx, translate);
      },
      response: () => ({ ok: true })
    },
    delete: {
      commandId: "catalog.variants.delete",
      schema: rawBodySchema,
      mapInput: async ({ parsed, ctx }) => {
        const { translate } = await resolveTranslations();
        const id = resolveCrudRecordId(parsed, ctx, translate);
        if (!id) throw new CrudHttpError(400, { error: translate("catalog.errors.id_required", "Variant id is required.") });
        return { id };
      },
      response: () => ({ ok: true })
    }
  }
});
const GET = crud.GET;
const POST = crud.POST;
const PUT = crud.PUT;
const DELETE = crud.DELETE;
const variantListItemSchema = z.object({
  id: z.string().uuid(),
  product_id: z.string().uuid().nullable().optional(),
  name: z.string().nullable().optional(),
  sku: z.string().nullable().optional(),
  barcode: z.string().nullable().optional(),
  gtin_type: z.string().nullable().optional(),
  hs_code: z.string().nullable().optional(),
  status_entry_id: z.string().uuid().nullable().optional(),
  is_default: z.boolean().nullable().optional(),
  is_active: z.boolean().nullable().optional(),
  weight_value: z.number().nullable().optional(),
  weight_unit: z.string().nullable().optional(),
  dimensions: z.record(z.string(), z.unknown()).nullable().optional(),
  metadata: z.record(z.string(), z.unknown()).nullable().optional(),
  option_values: z.record(z.string(), z.unknown()).nullable().optional(),
  custom_fieldset_code: z.string().nullable().optional(),
  default_media_id: z.string().uuid().nullable().optional(),
  default_media_url: z.string().nullable().optional(),
  created_at: z.string().nullable().optional(),
  updated_at: z.string().nullable().optional()
});
const openApi = createCatalogCrudOpenApi({
  resourceName: "Variant",
  pluralName: "Variants",
  querySchema: listSchema,
  listResponseSchema: createPagedListResponseSchema(variantListItemSchema),
  create: {
    schema: variantCreateSchema,
    description: "Creates a new product variant."
  },
  update: {
    schema: variantUpdateSchema,
    responseSchema: defaultOkResponseSchema,
    description: "Updates an existing variant by id."
  },
  del: {
    schema: z.object({ id: z.string().uuid() }),
    responseSchema: defaultOkResponseSchema,
    description: "Deletes a variant by id."
  }
});
export {
  DELETE,
  GET,
  POST,
  PUT,
  buildVariantFilters,
  metadata,
  openApi,
  stripPlaceholderId
};
//# sourceMappingURL=route.js.map
