import { NextResponse } from "next/server";
import { z } from "zod";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
import { CustomEntity, CustomFieldDef } from "@open-mercato/core/modules/entities/data/entities";
import { getEntityIds } from "@open-mercato/shared/lib/encryption/entityIds";
import { upsertCustomEntitySchema } from "@open-mercato/core/modules/entities/data/validators";
import { isSystemEntitySelectable } from "@open-mercato/shared/lib/entities/system-entities";
import { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from "@open-mercato/shared/lib/data/engine";
import { enforceCommandOptimisticLockWithGuards } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import {
  beginEntitiesMutationGuard,
  ENTITY_DEFINITION_RESOURCE_KIND
} from "./definitions.mutation-guard.js";
const CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND = ENTITY_DEFINITION_RESOURCE_KIND;
const metadata = {
  GET: { requireAuth: true },
  POST: { requireAuth: true, requireFeatures: ["entities.definitions.manage"] },
  DELETE: { requireAuth: true, requireFeatures: ["entities.definitions.manage"] }
};
async function GET(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth || !auth.tenantId || !auth.orgId && !auth.isSuperAdmin) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  const { resolve } = await createRequestContainer();
  const em = resolve("em");
  const AllEntities = getEntityIds();
  const generated = [];
  for (const modId of Object.keys(AllEntities)) {
    const entities = AllEntities[modId];
    for (const k of Object.keys(entities)) {
      const id = entities[k];
      if (!isSystemEntitySelectable(id)) continue;
      generated.push({ entityId: id, source: "code", label: id });
    }
  }
  const where = { isActive: true };
  where.$and = [
    { $or: [{ organizationId: auth.orgId ?? void 0 }, { organizationId: null }] },
    { $or: [{ tenantId: auth.tenantId ?? void 0 }, { tenantId: null }] }
  ];
  const customs = await em.find(CustomEntity, where, { orderBy: { entityId: "asc" } });
  const customByEntityId = /* @__PURE__ */ new Map();
  for (const c of customs) {
    const specificity = (c.organizationId ? 2 : 0) + (c.tenantId ? 1 : 0);
    const prev = customByEntityId.get(c.entityId);
    const prevSpec = prev ? (prev.organizationId ? 2 : 0) + (prev.tenantId ? 1 : 0) : -1;
    if (!prev || specificity > prevSpec) customByEntityId.set(c.entityId, c);
  }
  const custom = Array.from(customByEntityId.values()).filter((c) => isSystemEntitySelectable(c.entityId)).map((c) => ({
    entityId: c.entityId,
    source: "custom",
    label: c.label,
    description: c.description ?? void 0,
    labelField: c.labelField ?? void 0,
    defaultEditor: c.defaultEditor ?? void 0,
    showInSidebar: c.showInSidebar ?? false,
    updatedAt: c.updatedAt instanceof Date ? c.updatedAt.toISOString() : c.updatedAt ?? void 0
  }));
  const byId = /* @__PURE__ */ new Map();
  for (const g of generated) byId.set(g.entityId, g);
  for (const cu of custom) {
    const existing = byId.get(cu.entityId);
    byId.set(cu.entityId, { ...existing, ...cu, source: existing?.source ?? cu.source });
  }
  const defsWhere = { isActive: true };
  defsWhere.$and = [
    //{ $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] }, // the entities and custom fields are defined per tenant
    { tenantId: auth.tenantId ?? void 0 }
  ];
  const defs = await em.find(CustomFieldDef, defsWhere);
  const keySets = /* @__PURE__ */ new Map();
  for (const d of defs) {
    const eid = String(d.entityId);
    const k = String(d.key);
    if (!isSystemEntitySelectable(eid)) continue;
    const set = keySets.get(eid) || /* @__PURE__ */ new Set();
    set.add(k);
    keySets.set(eid, set);
  }
  const counts = {};
  for (const [eid, set] of keySets.entries()) counts[eid] = set.size;
  const items = Array.from(byId.values()).map((it) => ({ ...it, count: counts[it.entityId] || 0 }));
  return NextResponse.json({ items });
}
async function POST(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth || !auth.orgId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  let body;
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
  }
  const parsed = upsertCustomEntitySchema.safeParse(body);
  if (!parsed.success) {
    return NextResponse.json({ error: "Validation failed", details: parsed.error.flatten() }, { status: 400 });
  }
  const input = parsed.data;
  const container = await createRequestContainer();
  const { resolve } = container;
  const em = resolve("em");
  if (isOrmBackedSystemEntityId(em, input.entityId)) {
    return NextResponse.json(
      { error: "System entities cannot be registered as custom entities", code: SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, entityId: input.entityId },
      { status: 400 }
    );
  }
  const where = { entityId: input.entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null };
  let ent = await em.findOne(CustomEntity, where);
  if (ent) {
    try {
      await enforceCommandOptimisticLockWithGuards(container, {
        resourceKind: CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND,
        resourceId: ent.id,
        current: ent.updatedAt ?? null,
        request: req
      });
    } catch (lockError) {
      if (isCrudHttpError(lockError)) {
        return NextResponse.json(lockError.body, { status: lockError.status });
      }
      throw lockError;
    }
  }
  const guard = await beginEntitiesMutationGuard({
    container,
    auth,
    req,
    resourceKind: ENTITY_DEFINITION_RESOURCE_KIND,
    resourceId: ent ? ent.id : input.entityId,
    operation: ent ? "update" : "create",
    mutationPayload: input
  });
  if (guard.blockedResponse) return guard.blockedResponse;
  if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: /* @__PURE__ */ new Date() });
  ent.label = input.label;
  ent.description = input.description ?? null;
  ent.isActive = input.isActive ?? true;
  ent.labelField = input.labelField ?? ent.labelField ?? null;
  ent.defaultEditor = input.defaultEditor ?? ent.defaultEditor ?? null;
  ent.showInSidebar = input.showInSidebar ?? ent.showInSidebar ?? false;
  ent.updatedAt = /* @__PURE__ */ new Date();
  em.persist(ent);
  await em.flush();
  await guard.runAfterSuccess();
  try {
    const cache = (await createRequestContainer()).resolve("cache");
    if (cache) {
      await cache.deleteByTags([`nav:entities:${auth.tenantId || "null"}`]);
    }
  } catch {
  }
  return NextResponse.json({ ok: true, item: { id: ent.id, entityId: ent.entityId, label: ent.label, description: ent.description ?? void 0 } });
}
async function DELETE(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth || !auth.orgId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  let body;
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
  }
  const entityId = body?.entityId;
  if (!entityId) return NextResponse.json({ error: "entityId is required" }, { status: 400 });
  const container = await createRequestContainer();
  const { resolve } = container;
  const em = resolve("em");
  const where = { entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null };
  const ent = await em.findOne(CustomEntity, where);
  if (!ent) return NextResponse.json({ error: "Not found" }, { status: 404 });
  const guard = await beginEntitiesMutationGuard({
    container,
    auth,
    req,
    resourceKind: ENTITY_DEFINITION_RESOURCE_KIND,
    resourceId: ent.id,
    operation: "delete",
    mutationPayload: { entityId }
  });
  if (guard.blockedResponse) return guard.blockedResponse;
  ent.isActive = false;
  ent.updatedAt = /* @__PURE__ */ new Date();
  ent.deletedAt = ent.deletedAt ?? /* @__PURE__ */ new Date();
  em.persist(ent);
  await em.flush();
  await guard.runAfterSuccess();
  try {
    const cache = (await createRequestContainer()).resolve("cache");
    if (cache) {
      await cache.deleteByTags([`nav:entities:${auth.tenantId || "null"}`]);
    }
  } catch {
  }
  return NextResponse.json({ ok: true });
}
const entitySummarySchema = z.object({
  entityId: z.string(),
  source: z.enum(["code", "custom"]),
  label: z.string(),
  description: z.string().optional(),
  labelField: z.string().optional(),
  defaultEditor: z.string().optional(),
  showInSidebar: z.boolean().optional(),
  updatedAt: z.string().optional(),
  count: z.number()
});
const entityListResponseSchema = z.object({
  items: z.array(entitySummarySchema)
});
const deleteEntityRequestSchema = z.object({
  entityId: z.string()
});
const upsertCustomEntityResponseSchema = z.object({
  ok: z.literal(true),
  item: z.object({
    id: z.string().uuid(),
    entityId: z.string(),
    label: z.string(),
    description: z.string().optional()
  })
});
const openApi = {
  tag: "Entities",
  summary: "Manage custom entities",
  methods: {
    GET: {
      summary: "List available entities",
      description: "Returns generated and custom entities scoped to the caller with field counts per entity.",
      responses: [
        {
          status: 200,
          description: "List of entities",
          schema: entityListResponseSchema
        },
        {
          status: 401,
          description: "Missing authentication",
          schema: z.object({ error: z.string() })
        }
      ]
    },
    POST: {
      summary: "Upsert custom entity",
      description: "Creates or updates a tenant/org scoped custom entity definition.",
      requestBody: {
        contentType: "application/json",
        schema: upsertCustomEntitySchema
      },
      responses: [
        {
          status: 200,
          description: "Entity saved",
          schema: upsertCustomEntityResponseSchema
        },
        {
          status: 400,
          description: "Validation error",
          schema: z.object({
            error: z.string(),
            details: z.any().optional()
          })
        },
        {
          status: 401,
          description: "Missing authentication",
          schema: z.object({ error: z.string() })
        }
      ]
    },
    DELETE: {
      summary: "Soft delete custom entity",
      description: "Marks the specified custom entity inactive within the current scope.",
      requestBody: {
        contentType: "application/json",
        schema: deleteEntityRequestSchema
      },
      responses: [
        {
          status: 200,
          description: "Entity deleted",
          schema: z.object({ ok: z.boolean() })
        },
        {
          status: 400,
          description: "Missing entity id",
          schema: z.object({ error: z.string() })
        },
        {
          status: 401,
          description: "Missing authentication",
          schema: z.object({ error: z.string() })
        },
        {
          status: 404,
          description: "Entity not found in scope",
          schema: z.object({ error: z.string() })
        }
      ]
    }
  }
};
export {
  DELETE,
  GET,
  POST,
  metadata,
  openApi
};
//# sourceMappingURL=entities.js.map
