import { NextResponse } from "next/server";
import { z } from "zod";
import { logCrudAccess, makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { Tenant } from "@open-mercato/core/modules/directory/data/entities";
import { tenantCreateSchema, tenantUpdateSchema } from "@open-mercato/core/modules/directory/data/validators";
import { loadCustomFieldValues, buildCustomFieldFiltersFromQuery } from "@open-mercato/shared/lib/crud/custom-fields";
import { resolveRecordIdsForCustomFieldFilters } from "./tenant-cf-filter.js";
import { E } from "../../../../generated/entities.ids.generated.js";
import { tenantCrudEvents, tenantCrudIndexer } from "@open-mercato/core/modules/directory/commands/tenants";
import { directoryTag, directoryErrorSchema, directoryOkSchema, tenantListResponseSchema } from "../openapi.js";
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
const listQuerySchema = z.object({
  id: z.string().uuid().optional(),
  page: z.coerce.number().min(1).default(1),
  pageSize: z.coerce.number().min(1).max(100).default(50),
  search: z.string().optional(),
  sortField: z.enum(["name", "createdAt", "updatedAt"]).optional(),
  sortDir: z.enum(["asc", "desc"]).optional(),
  isActive: z.enum(["true", "false"]).optional()
}).passthrough();
const routeMetadata = {
  GET: { requireAuth: true, requireFeatures: ["directory.tenants.view"] },
  POST: { requireAuth: true, requireFeatures: ["directory.tenants.manage"] },
  PUT: { requireAuth: true, requireFeatures: ["directory.tenants.manage"] },
  DELETE: { requireAuth: true, requireFeatures: ["directory.tenants.manage"] }
};
const metadata = routeMetadata;
const rawBodySchema = z.object({}).passthrough();
const crud = makeCrudRoute({
  metadata: routeMetadata,
  orm: {
    entity: Tenant,
    idField: "id",
    orgField: null,
    tenantField: null,
    softDeleteField: "deletedAt"
  },
  events: tenantCrudEvents,
  indexer: tenantCrudIndexer,
  actions: {
    create: {
      commandId: "directory.tenants.create",
      schema: rawBodySchema,
      mapInput: ({ parsed }) => parsed,
      response: ({ result }) => ({ id: String(result.id) }),
      status: 201
    },
    update: {
      commandId: "directory.tenants.update",
      schema: rawBodySchema,
      mapInput: ({ parsed }) => parsed,
      response: () => ({ ok: true })
    },
    delete: {
      commandId: "directory.tenants.delete",
      response: () => ({ ok: true })
    }
  }
});
const toRow = (tenant, cf) => ({
  id: String(tenant.id),
  name: String(tenant.name),
  isActive: !!tenant.isActive,
  createdAt: tenant.createdAt ? tenant.createdAt.toISOString() : null,
  updatedAt: tenant.updatedAt ? tenant.updatedAt.toISOString() : null,
  ...cf
});
async function GET(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth) {
    return NextResponse.json({ items: [], total: 0, page: 1, pageSize: 50, totalPages: 1 }, { status: 401 });
  }
  const url = new URL(req.url);
  const parsed = listQuerySchema.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,
    sortField: url.searchParams.get("sortField") ?? void 0,
    sortDir: url.searchParams.get("sortDir") ?? void 0,
    isActive: url.searchParams.get("isActive") ?? void 0
  });
  if (!parsed.success) {
    return NextResponse.json(
      { error: "Invalid query parameters", details: parsed.error.flatten() },
      { status: 400 }
    );
  }
  const container = await createRequestContainer();
  const em = container.resolve("em");
  const { id, page, pageSize, search, sortField, sortDir, isActive } = parsed.data;
  const where = { deletedAt: null };
  if (id) where.id = id;
  if (search) {
    Object.assign(where, { name: { $ilike: `%${escapeLikePattern(search)}%` } });
  }
  const active = parseBooleanToken(isActive);
  if (active !== null) where.isActive = active;
  const fieldMap = { name: "name", createdAt: "createdAt", updatedAt: "updatedAt" };
  const orderBy = {};
  if (sortField) {
    const mapped = fieldMap[sortField] || "name";
    orderBy[mapped] = sortDir === "desc" ? "DESC" : "ASC";
  } else {
    orderBy.name = "ASC";
  }
  const rawQuery = Array.from(url.searchParams.keys()).reduce((acc, key) => {
    const values = url.searchParams.getAll(key);
    if (!values.length) return acc;
    acc[key] = values.length === 1 ? values[0] : values;
    return acc;
  }, {});
  const cfFilters = await buildCustomFieldFiltersFromQuery({
    entityId: E.directory.tenant,
    query: rawQuery,
    em,
    tenantId: auth.tenantId ?? null
  });
  const cfFilterEntries = Object.entries(cfFilters).map(([rawKey, condition]) => {
    const normalizedKey = rawKey.startsWith("cf:") ? rawKey.slice(3) : rawKey.replace(/^cf_/, "");
    return [normalizedKey, condition];
  });
  const loadCfValues = (tenants) => {
    const recordIds = tenants.map((tenant) => String(tenant.id));
    if (!recordIds.length) return Promise.resolve({});
    const tenantIdByRecord = {};
    const organizationIdByRecord = {};
    for (const rid of recordIds) {
      tenantIdByRecord[rid] = null;
      organizationIdByRecord[rid] = null;
    }
    return loadCustomFieldValues({
      em,
      entityId: E.directory.tenant,
      recordIds,
      tenantIdByRecord,
      organizationIdByRecord,
      tenantFallbacks: auth.tenantId ? [auth.tenantId] : []
    });
  };
  const start = (page - 1) * pageSize;
  let pageTenants;
  let total;
  let cfValues;
  if (cfFilterEntries.length) {
    const matchedIds = await resolveRecordIdsForCustomFieldFilters({
      em,
      entityId: E.directory.tenant,
      tenantId: auth.tenantId ?? null,
      filters: cfFilterEntries
    });
    const existingId = where.id;
    const idFilter = typeof existingId === "string" ? matchedIds.has(existingId) ? [existingId] : [] : Array.from(matchedIds);
    if (!idFilter.length) {
      total = 0;
      pageTenants = [];
      cfValues = {};
    } else {
      where.id = { $in: idFilter };
      const [rows, count] = await em.findAndCount(Tenant, where, { orderBy, limit: pageSize, offset: start });
      total = count;
      pageTenants = rows;
      cfValues = await loadCfValues(rows);
    }
  } else {
    const [rows, count] = await em.findAndCount(Tenant, where, { orderBy, limit: pageSize, offset: start });
    total = count;
    pageTenants = rows;
    cfValues = await loadCfValues(rows);
  }
  const items = pageTenants.map((tenant) => {
    const rid = String(tenant.id);
    const cf = cfValues[rid] ?? {};
    return toRow(tenant, cf);
  });
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
  await logCrudAccess({
    container,
    auth,
    request: req,
    items,
    idField: "id",
    resourceKind: "directory.tenant",
    organizationId: null,
    tenantId: auth.tenantId ?? null,
    query: rawQuery,
    accessType: id ? "read:item" : void 0
  });
  return NextResponse.json({ items, total, page, pageSize, totalPages });
}
const POST = crud.POST;
const PUT = crud.PUT;
const DELETE = crud.DELETE;
const tenantCreateResponseSchema = z.object({
  id: z.string().uuid()
});
const tenantDeleteRequestSchema = z.object({
  id: z.string().uuid()
});
const tenantGetDoc = {
  summary: "List tenants",
  description: "Returns tenants visible to the current user with optional search and pagination.",
  tags: [directoryTag],
  query: listQuerySchema,
  responses: [
    { status: 200, description: "Paged list of tenants.", schema: tenantListResponseSchema }
  ],
  errors: [
    { status: 400, description: "Invalid query parameters", schema: directoryErrorSchema },
    { status: 401, description: "Authentication required", schema: directoryErrorSchema }
  ]
};
const tenantPostDoc = {
  summary: "Create tenant",
  description: "Creates a new tenant and returns its identifier.",
  tags: [directoryTag],
  requestBody: {
    contentType: "application/json",
    schema: tenantCreateSchema,
    description: "Tenant name and optional activation flag."
  },
  responses: [
    { status: 201, description: "Tenant created.", schema: tenantCreateResponseSchema }
  ],
  errors: [
    { status: 400, description: "Validation failed", schema: directoryErrorSchema },
    { status: 401, description: "Authentication required", schema: directoryErrorSchema },
    { status: 403, description: "Missing directory.tenants.manage feature", schema: directoryErrorSchema }
  ]
};
const tenantPutDoc = {
  summary: "Update tenant",
  description: "Updates tenant properties such as name or activation state.",
  tags: [directoryTag],
  requestBody: {
    contentType: "application/json",
    schema: tenantUpdateSchema,
    description: "Tenant identifier with fields to update."
  },
  responses: [
    { status: 200, description: "Tenant updated.", schema: directoryOkSchema }
  ],
  errors: [
    { status: 400, description: "Validation failed", schema: directoryErrorSchema },
    { status: 401, description: "Authentication required", schema: directoryErrorSchema },
    { status: 403, description: "Missing directory.tenants.manage feature", schema: directoryErrorSchema }
  ]
};
const tenantDeleteDoc = {
  summary: "Delete tenant",
  description: "Soft deletes the tenant identified by id.",
  tags: [directoryTag],
  requestBody: {
    contentType: "application/json",
    schema: tenantDeleteRequestSchema,
    description: "Identifier of the tenant to remove."
  },
  responses: [
    { status: 200, description: "Tenant removed.", schema: directoryOkSchema }
  ],
  errors: [
    { status: 400, description: "Validation failed", schema: directoryErrorSchema },
    { status: 401, description: "Authentication required", schema: directoryErrorSchema },
    { status: 403, description: "Missing directory.tenants.manage feature", schema: directoryErrorSchema }
  ]
};
const openApi = {
  tag: directoryTag,
  summary: "Manage tenants",
  methods: {
    GET: tenantGetDoc,
    POST: tenantPostDoc,
    PUT: tenantPutDoc,
    DELETE: tenantDeleteDoc
  }
};
export {
  DELETE,
  GET,
  POST,
  PUT,
  metadata,
  openApi
};
//# sourceMappingURL=route.js.map
