import { NextResponse } from "next/server";
import { z } from "zod";
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { enforceCommandOptimisticLockWithGuards } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
import { BusinessRule } from "../../data/entities.js";
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
import {
  createBusinessRuleSchema,
  updateBusinessRuleSchema,
  createLocalizedBusinessRuleSchema,
  createLocalizedUpdateBusinessRuleSchema,
  ruleTypeSchema
} from "../../data/validators.js";
import {
  invalidateBusinessRuleDiscoveryCache,
  resolveBusinessRuleDiscoveryCache
} from "../../lib/rule-engine.js";
import { createLogger } from "@open-mercato/shared/lib/logger";
const logger = createLogger("business_rules").child({ component: "rules-api" });
const querySchema = z.looseObject({
  id: z.uuid().optional(),
  page: z.coerce.number().min(1).default(1),
  pageSize: z.coerce.number().min(1).max(100).default(50),
  search: z.string().optional(),
  ruleId: z.string().optional(),
  ruleType: ruleTypeSchema.optional(),
  entityType: z.string().optional(),
  eventType: z.string().optional(),
  enabled: z.coerce.boolean().optional(),
  ruleCategory: z.string().optional(),
  sortField: z.string().optional(),
  sortDir: z.enum(["asc", "desc"]).optional().default("desc")
});
const ruleListItemSchema = z.object({
  id: z.string().uuid(),
  ruleId: z.string(),
  ruleName: z.string(),
  description: z.string().nullable(),
  ruleType: ruleTypeSchema,
  ruleCategory: z.string().nullable(),
  entityType: z.string(),
  eventType: z.string().nullable(),
  enabled: z.boolean(),
  priority: z.number(),
  version: z.number(),
  effectiveFrom: z.string().nullable(),
  effectiveTo: z.string().nullable(),
  tenantId: z.string().uuid(),
  organizationId: z.string().uuid(),
  createdAt: z.string(),
  updatedAt: z.string()
});
const ruleListResponseSchema = z.object({
  items: z.array(ruleListItemSchema),
  total: z.number().int().nonnegative(),
  totalPages: z.number().int().positive()
});
const ruleCreateResponseSchema = z.object({
  id: z.string().uuid()
});
const okResponseSchema = z.object({
  ok: z.literal(true)
});
const errorResponseSchema = z.object({
  error: z.string()
});
const routeMetadata = {
  GET: { requireAuth: true, requireFeatures: ["business_rules.view"] },
  POST: { requireAuth: true, requireFeatures: ["business_rules.manage"] },
  PUT: { requireAuth: true, requireFeatures: ["business_rules.manage"] },
  DELETE: { requireAuth: true, requireFeatures: ["business_rules.manage"] }
};
const metadata = routeMetadata;
async function GET(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const url = new URL(req.url);
  const parsed = querySchema.safeParse({
    id: url.searchParams.get("id") || void 0,
    page: url.searchParams.get("page") || void 0,
    pageSize: url.searchParams.get("pageSize") || void 0,
    search: url.searchParams.get("search") || void 0,
    ruleId: url.searchParams.get("ruleId") || void 0,
    ruleType: url.searchParams.get("ruleType") || void 0,
    entityType: url.searchParams.get("entityType") || void 0,
    eventType: url.searchParams.get("eventType") || void 0,
    enabled: url.searchParams.get("enabled") || void 0,
    ruleCategory: url.searchParams.get("ruleCategory") || void 0,
    sortField: url.searchParams.get("sortField") || void 0,
    sortDir: url.searchParams.get("sortDir") || void 0
  });
  if (!parsed.success) {
    return NextResponse.json({ error: "Invalid query parameters" }, { status: 400 });
  }
  const container = await createRequestContainer();
  const em = container.resolve("em");
  const { id, page, pageSize, search, ruleId, ruleType, entityType, eventType, enabled, ruleCategory, sortField, sortDir } = parsed.data;
  const filters = {
    tenantId: auth.tenantId,
    organizationId: auth.orgId,
    deletedAt: null
  };
  if (id) filters.id = id;
  if (ruleId) filters.ruleId = { $ilike: `%${escapeLikePattern(ruleId)}%` };
  if (search) filters.ruleName = { $ilike: `%${escapeLikePattern(search)}%` };
  if (ruleType) filters.ruleType = ruleType;
  if (entityType) filters.entityType = entityType;
  if (eventType) filters.eventType = eventType;
  if (enabled !== void 0) filters.enabled = enabled;
  if (ruleCategory) filters.ruleCategory = ruleCategory;
  const sortFieldMap = {
    ruleId: "ruleId",
    ruleName: "ruleName",
    ruleType: "ruleType",
    entityType: "entityType",
    priority: "priority",
    createdAt: "createdAt",
    updatedAt: "updatedAt"
  };
  const orderByField = sortField && sortFieldMap[sortField] ? sortFieldMap[sortField] : "priority";
  const orderBy = { [orderByField]: sortDir, ruleId: "asc" };
  const [rows, count] = await em.findAndCount(
    BusinessRule,
    filters,
    {
      limit: pageSize,
      offset: (page - 1) * pageSize,
      orderBy
    }
  );
  const items = rows.map((rule) => ({
    id: rule.id,
    ruleId: rule.ruleId,
    ruleName: rule.ruleName,
    description: rule.description ?? null,
    ruleType: rule.ruleType,
    ruleCategory: rule.ruleCategory ?? null,
    entityType: rule.entityType,
    eventType: rule.eventType ?? null,
    enabled: rule.enabled,
    priority: rule.priority,
    version: rule.version,
    effectiveFrom: rule.effectiveFrom ? rule.effectiveFrom.toISOString() : null,
    effectiveTo: rule.effectiveTo ? rule.effectiveTo.toISOString() : null,
    tenantId: rule.tenantId,
    organizationId: rule.organizationId,
    createdAt: rule.createdAt.toISOString(),
    updatedAt: rule.updatedAt.toISOString()
  }));
  const totalPages = Math.max(1, Math.ceil(count / pageSize));
  return NextResponse.json({ items, total: count, totalPages });
}
async function POST(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const container = await createRequestContainer();
  const em = container.resolve("em");
  const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
  let body;
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
  }
  const payload = {
    ...body,
    tenantId: auth.tenantId,
    organizationId: auth.orgId,
    createdBy: auth.sub ?? auth.email ?? null
  };
  const { t } = await resolveTranslations();
  const schema = createLocalizedBusinessRuleSchema(t);
  const parsed = schema.safeParse(payload);
  if (!parsed.success) {
    const errors = parsed.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`);
    return NextResponse.json({ error: `Validation failed: ${errors.join(", ")}` }, { status: 400 });
  }
  const data = {
    ...parsed.data,
    conditionExpression: parsed.data.conditionExpression ?? null
  };
  const rule = em.create(BusinessRule, data);
  try {
    await em.persist(rule).flush();
    await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId);
  } catch (error) {
    logger.error("Failed to persist new rule", { err: error });
    return NextResponse.json(
      { error: t("business_rules.errors.createFailed") },
      { status: 500 }
    );
  }
  return NextResponse.json({ id: rule.id }, { status: 201 });
}
async function PUT(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const container = await createRequestContainer();
  const em = container.resolve("em");
  const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
  let body;
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
  }
  if (!body.id) {
    return NextResponse.json({ error: "Rule id is required" }, { status: 400 });
  }
  const payload = {
    ...body,
    updatedBy: auth.sub ?? auth.email ?? null
  };
  delete payload.tenantId;
  delete payload.organizationId;
  delete payload.createdBy;
  const { t } = await resolveTranslations();
  const schema = createLocalizedUpdateBusinessRuleSchema(t);
  const parsed = schema.safeParse(payload);
  if (!parsed.success) {
    const errors = parsed.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`);
    return NextResponse.json({ error: `Validation failed: ${errors.join(", ")}` }, { status: 400 });
  }
  const rule = await em.findOne(BusinessRule, {
    id: parsed.data.id,
    tenantId: auth.tenantId,
    organizationId: auth.orgId,
    deletedAt: null
  });
  if (!rule) {
    return NextResponse.json({ error: "Rule not found" }, { status: 404 });
  }
  try {
    await enforceCommandOptimisticLockWithGuards(container, {
      resourceKind: "business_rules.rule",
      resourceId: rule.id,
      current: rule.updatedAt ?? null,
      request: req
    });
  } catch (err) {
    if (isCrudHttpError(err)) {
      return NextResponse.json(err.body, { status: err.status });
    }
    throw err;
  }
  em.assign(rule, parsed.data);
  try {
    await em.persist(rule).flush();
    await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId);
  } catch (error) {
    logger.error("Failed to persist rule update", { err: error });
    return NextResponse.json(
      { error: t("business_rules.errors.updateFailed") },
      { status: 500 }
    );
  }
  return NextResponse.json({ ok: true });
}
async function DELETE(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  const url = new URL(req.url);
  const id = url.searchParams.get("id");
  if (!id) {
    return NextResponse.json({ error: "Rule id is required" }, { status: 400 });
  }
  const container = await createRequestContainer();
  const em = container.resolve("em");
  const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
  const rule = await em.findOne(BusinessRule, {
    id,
    tenantId: auth.tenantId,
    organizationId: auth.orgId,
    deletedAt: null
  });
  if (!rule) {
    return NextResponse.json({ error: "Rule not found" }, { status: 404 });
  }
  try {
    await enforceCommandOptimisticLockWithGuards(container, {
      resourceKind: "business_rules.rule",
      resourceId: rule.id,
      current: rule.updatedAt ?? null,
      request: req
    });
  } catch (err) {
    if (isCrudHttpError(err)) {
      return NextResponse.json(err.body, { status: err.status });
    }
    throw err;
  }
  rule.deletedAt = /* @__PURE__ */ new Date();
  await em.persist(rule).flush();
  await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId);
  return NextResponse.json({ ok: true });
}
const openApi = {
  tag: "Business Rules",
  summary: "Business rule management",
  methods: {
    GET: {
      summary: "List business rules",
      description: "Returns business rules for the current tenant and organization with filtering and pagination.",
      query: querySchema,
      responses: [
        { status: 200, description: "Business rules collection", schema: ruleListResponseSchema }
      ],
      errors: [
        { status: 400, description: "Invalid query parameters", schema: errorResponseSchema },
        { status: 401, description: "Unauthorized", schema: errorResponseSchema }
      ]
    },
    POST: {
      summary: "Create business rule",
      description: "Creates a new business rule for the current tenant and organization.",
      requestBody: {
        contentType: "application/json",
        schema: createBusinessRuleSchema
      },
      responses: [
        {
          status: 201,
          description: "Business rule created",
          schema: ruleCreateResponseSchema
        }
      ],
      errors: [
        { status: 400, description: "Invalid payload", schema: errorResponseSchema },
        { status: 401, description: "Unauthorized", schema: errorResponseSchema }
      ]
    },
    PUT: {
      summary: "Update business rule",
      description: "Updates an existing business rule.",
      requestBody: {
        contentType: "application/json",
        schema: updateBusinessRuleSchema
      },
      responses: [
        {
          status: 200,
          description: "Business rule updated",
          schema: okResponseSchema
        }
      ],
      errors: [
        { status: 400, description: "Invalid payload", schema: errorResponseSchema },
        { status: 401, description: "Unauthorized", schema: errorResponseSchema },
        { status: 404, description: "Business rule not found", schema: errorResponseSchema }
      ]
    },
    DELETE: {
      summary: "Delete business rule",
      description: "Soft deletes a business rule by identifier.",
      query: z.object({ id: z.string().uuid().describe("Business rule identifier") }),
      responses: [
        { status: 200, description: "Business rule deleted", schema: okResponseSchema }
      ],
      errors: [
        { status: 400, description: "Invalid identifier", schema: errorResponseSchema },
        { status: 401, description: "Unauthorized", schema: errorResponseSchema },
        { status: 404, description: "Business rule not found", schema: errorResponseSchema }
      ]
    }
  }
};
export {
  DELETE,
  GET,
  POST,
  PUT,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
