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 { CustomerUser, CustomerUserRole, CustomerRole } from "@open-mercato/core/modules/customer_accounts/data/entities";
import { adminCreateUserSchema } from "@open-mercato/core/modules/customer_accounts/data/validators";
import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
import { findAndCountWithDecryption, findWithDecryption, findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { isOwnedCompanyEntity } from "@open-mercato/core/modules/customer_accounts/lib/customerEntityOwnership";
import { lookupHashCandidates } from "@open-mercato/shared/lib/encryption/aes";
import { E } from "../../../../generated/entities.ids.generated.js";
import { resolveSearchConfig } from "@open-mercato/shared/lib/search/config";
import { tokenizeText } from "@open-mercato/shared/lib/search/tokenize";
import { sql } from "kysely";
const EMAIL_LIKE_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const metadata = {};
async function GET(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth) {
    return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
  }
  const container = await createRequestContainer();
  const rbacService = container.resolve("rbacService");
  const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.view"], { tenantId: auth.tenantId, organizationId: auth.orgId });
  if (!hasAccess) {
    return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
  }
  const em = container.resolve("em");
  const url = new URL(req.url);
  const page = Math.max(1, parseInt(url.searchParams.get("page") || "1"));
  const pageSize = Math.min(100, Math.max(1, parseInt(url.searchParams.get("pageSize") || "25")));
  const status = url.searchParams.get("status");
  const customerEntityId = url.searchParams.get("customerEntityId");
  const personEntityId = url.searchParams.get("personEntityId");
  const roleId = url.searchParams.get("roleId");
  const search = url.searchParams.get("search");
  const where = {
    tenantId: auth.tenantId,
    organizationId: auth.orgId,
    deletedAt: null
  };
  if (status === "active") {
    where.isActive = true;
    where.$or = [{ lockedUntil: null }, { lockedUntil: { $lt: /* @__PURE__ */ new Date() } }];
  } else if (status === "inactive") {
    where.isActive = false;
  } else if (status === "locked") {
    where.lockedUntil = { $gt: /* @__PURE__ */ new Date() };
  }
  if (customerEntityId) {
    where.customerEntityId = customerEntityId;
  }
  if (personEntityId) {
    where.personEntityId = personEntityId;
  }
  if (search) {
    const trimmedSearch = search.trim();
    const searchFilter = [];
    const matchedIds = await findCustomerUserIdsBySearchTokens(em, E.customer_accounts.customer_user, trimmedSearch, auth.tenantId);
    if (matchedIds && matchedIds.length > 0) {
      searchFilter.push({ id: { $in: matchedIds } });
    }
    if (EMAIL_LIKE_PATTERN.test(search)) {
      searchFilter.push({ emailHash: { $in: lookupHashCandidates(search) } });
    }
    if (searchFilter.length > 0) {
      if (where.$or) {
        where.$and = [{ $or: where.$or }, { $or: searchFilter }];
        delete where.$or;
      } else {
        where.$or = searchFilter;
      }
    } else {
      return NextResponse.json({
        ok: true,
        items: [],
        total: 0,
        totalPages: 1,
        page
      });
    }
  }
  let userIds = null;
  if (roleId) {
    const scopedRole = await findOneWithDecryption(
      em,
      CustomerRole,
      { id: roleId, tenantId: auth.tenantId, organizationId: auth.orgId, deletedAt: null },
      void 0,
      { tenantId: auth.tenantId, organizationId: auth.orgId }
    );
    if (!scopedRole) {
      return NextResponse.json({
        ok: true,
        items: [],
        total: 0,
        totalPages: 1,
        page
      });
    }
    const roleLinks = await findWithDecryption(
      em,
      CustomerUserRole,
      { role: roleId, deletedAt: null },
      void 0,
      { tenantId: auth.tenantId, organizationId: auth.orgId }
    );
    userIds = roleLinks.map((link) => link.user?.id || link.user);
    if (userIds.length === 0) {
      return NextResponse.json({
        ok: true,
        items: [],
        total: 0,
        totalPages: 1,
        page
      });
    }
    where.id = { $in: userIds };
  }
  const offset = (page - 1) * pageSize;
  const [users, total] = await findAndCountWithDecryption(
    em,
    CustomerUser,
    where,
    {
      orderBy: { createdAt: "DESC" },
      limit: pageSize,
      offset
    },
    { tenantId: auth.tenantId, organizationId: auth.orgId }
  );
  const pageUserIds = users.map((user) => user.id);
  const userRoleLinks = pageUserIds.length > 0 ? await findWithDecryption(
    em,
    CustomerUserRole,
    { user: { $in: pageUserIds }, deletedAt: null },
    { populate: ["role"] },
    { tenantId: auth.tenantId, organizationId: auth.orgId }
  ) : [];
  const rolesByUserId = /* @__PURE__ */ new Map();
  for (const link of userRoleLinks) {
    const linkUserId = link.user?.id ?? link.user;
    const role = link.role;
    const bucket = rolesByUserId.get(linkUserId);
    const entry = { id: role.id, name: role.name, slug: role.slug };
    if (bucket) bucket.push(entry);
    else rolesByUserId.set(linkUserId, [entry]);
  }
  const items = users.map((user) => ({
    id: user.id,
    email: user.email,
    displayName: user.displayName,
    emailVerified: !!user.emailVerifiedAt,
    isActive: user.isActive,
    lockedUntil: user.lockedUntil || null,
    lastLoginAt: user.lastLoginAt || null,
    customerEntityId: user.customerEntityId || null,
    personEntityId: user.personEntityId || null,
    createdAt: user.createdAt,
    updatedAt: user.updatedAt || null,
    roles: rolesByUserId.get(user.id) ?? []
  }));
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
  return NextResponse.json({
    ok: true,
    items,
    total,
    totalPages,
    page
  });
}
async function POST(req) {
  const auth = await getAuthFromRequest(req);
  if (!auth) {
    return NextResponse.json({ ok: false, error: "Authentication required" }, { status: 401 });
  }
  const container = await createRequestContainer();
  const rbacService = container.resolve("rbacService");
  const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ["customer_accounts.manage"], { tenantId: auth.tenantId, organizationId: auth.orgId });
  if (!hasAccess) {
    return NextResponse.json({ ok: false, error: "Insufficient permissions" }, { status: 403 });
  }
  let body;
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ ok: false, error: "Invalid request body" }, { status: 400 });
  }
  const parsed = adminCreateUserSchema.safeParse(body);
  if (!parsed.success) {
    return NextResponse.json({ ok: false, error: "Validation failed", details: parsed.error.flatten().fieldErrors }, { status: 400 });
  }
  const em = container.resolve("em");
  const customerUserService = container.resolve("customerUserService");
  const existing = await customerUserService.findByEmail(parsed.data.email, auth.tenantId);
  if (existing) {
    return NextResponse.json({ ok: false, error: "A user with this email already exists" }, { status: 409 });
  }
  let resolvedRoles = [];
  if (parsed.data.roleIds && parsed.data.roleIds.length > 0) {
    const requestedRoleIds = parsed.data.roleIds;
    const validRoles = await findWithDecryption(
      em,
      CustomerRole,
      {
        id: { $in: requestedRoleIds },
        tenantId: auth.tenantId,
        organizationId: auth.orgId,
        deletedAt: null
      },
      void 0,
      { tenantId: auth.tenantId, organizationId: auth.orgId }
    );
    if (validRoles.length !== requestedRoleIds.length) {
      const foundIds = new Set(validRoles.map((role) => role.id));
      const missingId = requestedRoleIds.find((roleId) => !foundIds.has(roleId));
      return NextResponse.json({ ok: false, error: `Role ${missingId} not found` }, { status: 400 });
    }
    resolvedRoles = validRoles;
  }
  if (parsed.data.customerEntityId) {
    const owned = await isOwnedCompanyEntity(em, parsed.data.customerEntityId, {
      tenantId: auth.tenantId,
      organizationId: auth.orgId
    });
    if (!owned) {
      return NextResponse.json({ ok: false, error: "Company not found" }, { status: 400 });
    }
  }
  const user = await customerUserService.createUser(
    parsed.data.email,
    parsed.data.password,
    parsed.data.displayName,
    { tenantId: auth.tenantId, organizationId: auth.orgId }
  );
  user.emailVerifiedAt = /* @__PURE__ */ new Date();
  await em.transactional(async (tx) => {
    tx.persist(user);
    await tx.flush();
    if (parsed.data.customerEntityId) {
      await tx.nativeUpdate(CustomerUser, { id: user.id }, { customerEntityId: parsed.data.customerEntityId });
    }
    for (const role of resolvedRoles) {
      const userRole = tx.create(CustomerUserRole, {
        user,
        role,
        createdAt: /* @__PURE__ */ new Date()
      });
      tx.persist(userRole);
    }
  });
  void emitCustomerAccountsEvent("customer_accounts.user.created", {
    id: user.id,
    email: user.email,
    tenantId: auth.tenantId,
    organizationId: auth.orgId,
    createdBy: auth.sub
  }).catch(() => void 0);
  return NextResponse.json({
    ok: true,
    user: { id: user.id, email: user.email, displayName: user.displayName }
  }, { status: 201 });
}
const roleSchema = z.object({ id: z.string().uuid(), name: z.string(), slug: z.string() });
const userSchema = z.object({
  id: z.string().uuid(),
  email: z.string(),
  displayName: z.string(),
  emailVerified: z.boolean(),
  isActive: z.boolean(),
  lockedUntil: z.string().datetime().nullable(),
  lastLoginAt: z.string().datetime().nullable(),
  customerEntityId: z.string().uuid().nullable(),
  personEntityId: z.string().uuid().nullable(),
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime().nullable(),
  roles: z.array(roleSchema)
});
const successSchema = z.object({
  ok: z.literal(true),
  user: z.object({ id: z.string().uuid(), email: z.string(), displayName: z.string() })
});
const errorSchema = z.object({ ok: z.literal(false), error: z.string() });
async function findCustomerUserIdsBySearchTokens(em, entityType, search, tenantScope, field) {
  const trimmed = search.trim();
  if (!trimmed) return null;
  const searchConfig = resolveSearchConfig();
  if (!searchConfig.enabled) return [];
  const { hashes } = tokenizeText(trimmed, searchConfig);
  if (!hashes.length) return [];
  const db = em.getKysely();
  let query = db.selectFrom("search_tokens").select("entity_id").where("entity_type", "=", entityType).where("token_hash", "in", hashes).groupBy("entity_id").having(sql`count(distinct token_hash) >= ${hashes.length}`);
  if (field) {
    query = query.where("field", "=", field);
  }
  if (tenantScope !== void 0) {
    query = query.where(sql`tenant_id is not distinct from ${tenantScope}`);
  }
  const rows = await query.execute();
  return rows.map((row) => typeof row.entity_id === "string" ? row.entity_id : null).filter((id) => typeof id === "string" && id.length > 0);
}
const getMethodDoc = {
  summary: "List customer users (admin)",
  description: "Returns a paginated list of customer users with roles. Supports filtering by status, company, role, and search.",
  tags: ["Customer Accounts Admin"],
  query: z.object({
    page: z.number().int().positive().optional(),
    pageSize: z.number().int().positive().max(100).optional(),
    status: z.enum(["active", "inactive", "locked"]).optional(),
    customerEntityId: z.string().uuid().optional(),
    roleId: z.string().uuid().optional(),
    search: z.string().optional()
  }),
  responses: [{
    status: 200,
    description: "Paginated user list",
    schema: z.object({ ok: z.literal(true), items: z.array(userSchema), total: z.number(), totalPages: z.number(), page: z.number() })
  }],
  errors: [
    { status: 401, description: "Not authenticated", schema: errorSchema },
    { status: 403, description: "Insufficient permissions", schema: errorSchema }
  ]
};
const postMethodDoc = {
  summary: "Create customer user (admin)",
  description: "Creates a new customer user directly. Staff-initiated, bypasses signup flow.",
  tags: ["Customer Accounts Admin"],
  requestBody: { schema: adminCreateUserSchema },
  responses: [{ status: 201, description: "User created", schema: successSchema }],
  errors: [
    { status: 400, description: "Validation failed", schema: errorSchema },
    { status: 401, description: "Not authenticated", schema: errorSchema },
    { status: 403, description: "Insufficient permissions", schema: errorSchema },
    { status: 409, description: "Email already exists", schema: errorSchema }
  ]
};
const openApi = {
  summary: "Customer user management (admin)",
  methods: {
    GET: getMethodDoc,
    POST: postMethodDoc
  }
};
export {
  GET,
  POST,
  metadata,
  openApi
};
//# sourceMappingURL=users.js.map
