import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
import { OM_LOG_LEVEL_ENV, resetLogLevelCache } from "@open-mercato/shared/lib/logger";
import { resetServerLoggerCache } from "@open-mercato/shared/lib/logger";
import { hash } from "bcryptjs";
import { User, Role, UserRole } from "@open-mercato/core/modules/auth/data/entities";
import { Tenant, Organization } from "@open-mercato/core/modules/directory/data/entities";
import { rebuildHierarchyForTenant } from "@open-mercato/core/modules/directory/lib/hierarchy";
import { ensureRoles, setupInitialTenant, ensureDefaultRoleAcls, ensureCustomRoleAcls, OrgSlugExistsError, DerivedUserPasswordRequiredError } from "./lib/setup-app.js";
import { normalizeTenantId } from "./lib/tenantAccess.js";
import { computeEmailHash, emailHashLookupValues } from "./lib/emailHash.js";
import { findWithDecryption, findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { isTenantDataEncryptionEnabled } from "@open-mercato/shared/lib/encryption/toggles";
import { createKmsService } from "@open-mercato/shared/lib/encryption/kms";
import { TenantDataEncryptionService } from "@open-mercato/shared/lib/encryption/tenantDataEncryptionService";
import { decryptWithAesGcm } from "@open-mercato/shared/lib/encryption/aes";
import { env } from "process";
import crypto from "node:crypto";
import { formatPasswordRequirements, getPasswordPolicy, validatePassword } from "@open-mercato/shared/lib/auth/passwordPolicy";
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
import { getCliModules } from "@open-mercato/shared/modules/registry";
async function resolveTenantScopedRole(em, name, normalizedTenantId) {
  const existing = await em.findOne(Role, { name, tenantId: normalizedTenantId });
  if (existing) return existing;
  const role = em.create(Role, { name, tenantId: normalizedTenantId, createdAt: /* @__PURE__ */ new Date() });
  await em.persist(role).flush();
  return role;
}
const addUser = {
  command: "add-user",
  async run(rest) {
    const args = {};
    for (let i = 0; i < rest.length; i += 2) {
      const k = rest[i]?.replace(/^--/, "");
      const v = rest[i + 1];
      if (k) args[k] = v;
    }
    const email = args.email;
    const password = args.password;
    const organizationId = String(args.organizationId ?? args.orgId ?? args.org);
    const rolesCsv = (args.roles ?? "").trim();
    if (!email || !password || !organizationId) {
      console.error("Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]");
      return;
    }
    if (!ensurePasswordPolicy(password)) return;
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    const org = await findOneWithDecryption(
      em,
      Organization,
      { id: organizationId },
      { populate: ["tenant"] },
      { tenantId: null, organizationId }
    ) ?? null;
    if (!org) throw new Error("Organization not found");
    const orgTenantId = org.tenant?.id ? String(org.tenant.id) : null;
    const normalizedTenantId = normalizeTenantId(orgTenantId ?? null) ?? null;
    const u = em.create(User, {
      email,
      emailHash: computeEmailHash(email),
      passwordHash: await hash(password, 10),
      isConfirmed: true,
      organizationId: org.id,
      tenantId: org.tenant.id
    });
    await em.persist(u).flush();
    if (rolesCsv) {
      const names = rolesCsv.split(",").map((s) => s.trim()).filter(Boolean);
      for (const name of names) {
        const role = await resolveTenantScopedRole(em, name, normalizedTenantId);
        const link = em.create(UserRole, { user: u, role });
        await em.persist(link).flush();
      }
    }
    console.log("User created with id", u.id);
  }
};
function parseArgs(rest) {
  const args = {};
  for (let i = 0; i < rest.length; i++) {
    const a = rest[i];
    if (!a) continue;
    if (a.startsWith("--")) {
      const [k, v] = a.replace(/^--/, "").split("=");
      if (v !== void 0) args[k] = v;
      else if (rest[i + 1] && !rest[i + 1].startsWith("--")) {
        args[k] = rest[i + 1];
        i++;
      } else args[k] = true;
    }
  }
  return args;
}
function normalizeKeyInput(value) {
  return value.trim().replace(/(?:^['"]|['"]$)/g, "");
}
function hashSecret(value) {
  if (!value) return null;
  return crypto.createHash("sha256").update(normalizeKeyInput(value)).digest("hex").slice(0, 12);
}
function ensurePasswordPolicy(password) {
  const policy = getPasswordPolicy();
  const result = validatePassword(password, policy);
  if (result.ok) return true;
  const requirements = formatPasswordRequirements(policy, (_key, fallback) => fallback);
  const suffix = requirements ? `: ${requirements}` : "";
  console.error(`Password does not meet the requirements${suffix}.`);
  return false;
}
async function withEncryptionDebugDisabled(fn) {
  const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG;
  process.env.TENANT_DATA_ENCRYPTION_DEBUG = "no";
  try {
    return await fn();
  } finally {
    if (previous === void 0) {
      delete process.env.TENANT_DATA_ENCRYPTION_DEBUG;
    } else {
      process.env.TENANT_DATA_ENCRYPTION_DEBUG = previous;
    }
  }
}
class DerivedKeyKmsService {
  constructor(secret) {
    this.root = crypto.createHash("sha256").update(normalizeKeyInput(secret)).digest();
  }
  isHealthy() {
    return true;
  }
  deriveKey(tenantId) {
    const iterations = 31e4;
    const keyLength = 32;
    const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, "sha512");
    return derived.toString("base64");
  }
  async getTenantDek(tenantId) {
    if (!tenantId) return null;
    return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() };
  }
  async createTenantDek(tenantId) {
    return this.getTenantDek(tenantId);
  }
}
function fingerprintDek(dek) {
  if (!dek?.key) return null;
  return crypto.createHash("sha256").update(dek.key).digest("hex").slice(0, 12);
}
function decryptWithOldKey(payload, dek) {
  if (!dek?.key) return null;
  return decryptWithAesGcm(payload, dek.key);
}
const seedRoles = {
  command: "seed-roles",
  async run(rest) {
    const args = {};
    for (let i = 0; i < rest.length; i += 2) {
      const key = rest[i]?.replace(/^--/, "");
      if (!key) continue;
      const value = rest[i + 1];
      if (value) args[key] = value;
    }
    const tenantId = args.tenantId ?? args.tenant ?? args.tenant_id ?? null;
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    if (tenantId) {
      await ensureRoles(em, { tenantId });
      console.log("\u{1F6E1}\uFE0F Roles ensured for tenant", tenantId);
      return;
    }
    const tenants = await em.find(Tenant, {});
    if (!tenants.length) {
      console.log("No tenants found; nothing to seed.");
      return;
    }
    for (const tenant of tenants) {
      const id = tenant.id ? String(tenant.id) : null;
      if (!id) continue;
      await ensureRoles(em, { tenantId: id });
      console.log("\u{1F6E1}\uFE0F Roles ensured for tenant", id);
    }
  }
};
const rotateEncryptionKey = {
  command: "rotate-encryption-key",
  async run(rest) {
    const args = parseArgs(rest);
    const tenantId = args.tenantId ?? args.tenant ?? args.tenant_id ?? null;
    const organizationId = args.organizationId ?? args.orgId ?? args.org ?? null;
    const oldKey = args["old-key"] ?? args.oldKey ?? null;
    const dryRun = Boolean(args["dry-run"] || args.dry);
    const debug = Boolean(args.debug);
    const rotate = Boolean(oldKey);
    if (rotate && !tenantId) {
      console.warn(
        "\u26A0\uFE0F  Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant."
      );
    }
    if (!isTenantDataEncryptionEnabled()) {
      console.error("TENANT_DATA_ENCRYPTION is disabled; aborting.");
      return;
    }
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    const encryptionService = new TenantDataEncryptionService(em, { kms: createKmsService() });
    const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null;
    if (debug) {
      console.log("[rotate-encryption-key]", {
        hasOldKey: Boolean(oldKey),
        rotate,
        tenantId: tenantId ?? null,
        organizationId: organizationId ?? null
      });
      console.log("[rotate-encryption-key] key fingerprints", {
        oldKey: hashSecret(oldKey),
        currentKey: hashSecret(process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY)
      });
    }
    if (!encryptionService.isEnabled()) {
      console.error("Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.");
      return;
    }
    const conn = em.getConnection?.();
    if (!conn || typeof conn.execute !== "function") {
      console.error("Unable to access raw connection; aborting.");
      return;
    }
    const meta = em?.getMetadata?.()?.get?.(User);
    const tableName = meta?.tableName || "users";
    const schema = meta?.schema;
    const qualifiedTable = schema ? `"${schema}"."${tableName}"` : `"${tableName}"`;
    const isEncryptedPayload = (value) => {
      if (typeof value !== "string") return false;
      const parts = value.split(":");
      return parts.length === 4 && parts[3] === "v1";
    };
    const printedDek = /* @__PURE__ */ new Set();
    const oldDekCache = /* @__PURE__ */ new Map();
    const processScope = async (scopeTenantId, scopeOrganizationId) => {
      if (debug && !printedDek.has(scopeTenantId)) {
        printedDek.add(scopeTenantId);
        const [oldDek, newDek] = await Promise.all([
          oldKms?.getTenantDek(scopeTenantId) ?? Promise.resolve(null),
          encryptionService.getDek(scopeTenantId)
        ]);
        console.log("[rotate-encryption-key] dek fingerprints", {
          tenantId: scopeTenantId,
          oldKey: fingerprintDek(oldDek),
          currentKey: fingerprintDek(newDek)
        });
      }
      const rawRows = await conn.execute(
        `select id, email, email_hash from ${qualifiedTable} where tenant_id = ? and organization_id = ?`,
        [scopeTenantId, scopeOrganizationId]
      );
      const rows = Array.isArray(rawRows) ? rawRows : [];
      const pending = rotate ? rows : rows.filter((row) => !isEncryptedPayload(row?.email));
      if (!pending.length) return 0;
      console.log(
        `Found ${pending.length} auth user records to process for org=${scopeOrganizationId}${dryRun ? " (dry-run)" : ""}.`
      );
      if (dryRun) return 0;
      const ids = pending.map((row) => String(row.id));
      const users = rotate ? await em.find(
        User,
        { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId }
      ) : await findWithDecryption(
        em,
        User,
        { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },
        {},
        { tenantId: scopeTenantId, organizationId: scopeOrganizationId, encryptionService }
      );
      const usersById = new Map(users.map((user) => [String(user.id), user]));
      let updated = 0;
      for (const row of pending) {
        const user = usersById.get(String(row.id));
        if (!user) continue;
        const rawEmail = typeof row.email === "string" ? row.email : String(row.email ?? "");
        if (!rawEmail) continue;
        if (rotate && (!isEncryptedPayload(rawEmail) || !oldKms)) {
          continue;
        }
        let plainEmail = rawEmail;
        if (rotate && isEncryptedPayload(rawEmail) && oldKms) {
          if (debug) {
            console.log("[rotate-encryption-key] decrypting", {
              userId: row.id,
              tenantId: scopeTenantId,
              organizationId: scopeOrganizationId
            });
          }
          let oldDek = oldDekCache.get(scopeTenantId) ?? null;
          if (!oldDekCache.has(scopeTenantId)) {
            oldDek = await oldKms.getTenantDek(scopeTenantId);
            oldDekCache.set(scopeTenantId, oldDek);
          }
          const maybeEmail = decryptWithOldKey(rawEmail, oldDek);
          if (typeof maybeEmail !== "string" || isEncryptedPayload(maybeEmail)) continue;
          plainEmail = maybeEmail;
        }
        if (!plainEmail) continue;
        const encrypted = await encryptionService.encryptEntityPayload(
          "auth:user",
          { email: plainEmail },
          scopeTenantId,
          scopeOrganizationId
        );
        const nextEmail = encrypted.email;
        if (nextEmail && nextEmail !== user.email) {
          user.email = nextEmail;
          user.emailHash = encrypted.emailHash ?? computeEmailHash(plainEmail);
          em.persist(user);
          updated += 1;
        }
      }
      if (updated > 0) {
        await em.flush();
      }
      return updated;
    };
    if (tenantId && organizationId) {
      const updated = await processScope(String(tenantId), String(organizationId));
      if (!updated) {
        console.log("All auth user emails already encrypted for the selected scope.");
      } else {
        console.log(`Encrypted ${updated} auth user email(s).`);
      }
      return;
    }
    const organizations = await em.find(Organization, {});
    if (!organizations.length) {
      console.log("No organizations found; nothing to encrypt.");
      return;
    }
    let total = 0;
    for (const org of organizations) {
      const scopeTenantId = org.tenant?.id ? String(org.tenant.id) : org.tenant.id ? String(org.tenant.id) : null;
      const scopeOrganizationId = org.id ? String(org.id) : null;
      if (!scopeTenantId || !scopeOrganizationId) continue;
      total += await processScope(scopeTenantId, scopeOrganizationId);
    }
    if (total > 0) {
      console.log(`Encrypted ${total} auth user email(s) across all organizations.`);
    } else {
      console.log("All auth user emails already encrypted across all organizations.");
    }
  }
};
const addOrganization = {
  command: "add-org",
  async run(rest) {
    const args = {};
    for (let i = 0; i < rest.length; i += 2) {
      const k = rest[i]?.replace(/^--/, "");
      const v = rest[i + 1];
      if (k) args[k] = v;
    }
    const name = args.name || args.orgName;
    if (!name) {
      console.error("Usage: mercato auth add-org --name <organization name>");
      return;
    }
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    const tenant = em.create(Tenant, { name: `${name} Tenant` });
    await em.persist(tenant).flush();
    const org = em.create(Organization, { name, tenant });
    await em.persist(org).flush();
    await rebuildHierarchyForTenant(em, String(tenant.id));
    console.log("Organization created with id", org.id, "in tenant", tenant.id);
  }
};
const SETUP_USAGE = "Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--orgSlug <slug>] [--roles superadmin,admin,employee] [--skip-password-policy] [--include-demo-users] [--with-examples] [--json]";
const setupApp = {
  command: "setup",
  async run(rest) {
    const args = parseArgs(rest);
    const orgName = typeof args.orgName === "string" ? args.orgName : typeof args.name === "string" ? args.name : void 0;
    const email = typeof args.email === "string" ? args.email : void 0;
    const password = typeof args.password === "string" ? args.password : void 0;
    const orgSlugRaw = typeof args.orgSlug === "string" ? args.orgSlug : typeof args.slug === "string" ? args.slug : void 0;
    const orgSlug = orgSlugRaw && orgSlugRaw.trim().length ? orgSlugRaw.trim() : void 0;
    if (orgSlug !== void 0 && !ORG_SLUG_PATTERN.test(orgSlug)) {
      process.stderr.write(`Invalid --orgSlug: must match ${ORG_SLUG_PATTERN.source} (lowercase, digits, dashes; 1-63 chars; cannot start or end with a dash)
`);
      process.exitCode = 2;
      return;
    }
    const rolesCsv = typeof args.roles === "string" ? args.roles.trim() : "superadmin,admin,employee";
    const skipPasswordPolicyRaw = args["skip-password-policy"] ?? args.skipPasswordPolicy ?? args["allow-weak-password"] ?? args.allowWeakPassword;
    const skipPasswordPolicy = typeof skipPasswordPolicyRaw === "boolean" ? skipPasswordPolicyRaw : parseBooleanToken(typeof skipPasswordPolicyRaw === "string" ? skipPasswordPolicyRaw : null) ?? false;
    const withExamplesRaw = args["with-examples"] ?? args.withExamples;
    const withExamples = typeof withExamplesRaw === "boolean" ? withExamplesRaw : parseBooleanToken(typeof withExamplesRaw === "string" ? withExamplesRaw : null) ?? false;
    const includeDemoUsersRaw = args["include-demo-users"] ?? args.includeDemoUsers;
    const includeDemoUsers = typeof includeDemoUsersRaw === "boolean" ? includeDemoUsersRaw : parseBooleanToken(typeof includeDemoUsersRaw === "string" ? includeDemoUsersRaw : null) ?? false;
    const jsonModeRaw = args.json;
    const jsonMode = typeof jsonModeRaw === "boolean" ? jsonModeRaw : parseBooleanToken(typeof jsonModeRaw === "string" ? jsonModeRaw : null) ?? false;
    if (!orgName || !email || !password) {
      process.stderr.write(`${SETUP_USAGE}
`);
      process.exitCode = 2;
      return;
    }
    if (!skipPasswordPolicy && !ensurePasswordPolicy(password)) {
      process.exitCode = 2;
      return;
    }
    if (skipPasswordPolicy && !jsonMode) {
      console.warn("\u26A0\uFE0F  Password policy validation skipped for setup.");
    }
    let restoreConsole = null;
    if (jsonMode) {
      process.env.OM_CLI_QUIET = process.env.OM_CLI_QUIET ?? "1";
      const originalLog = console.log;
      const originalInfo = console.info;
      console.log = () => void 0;
      console.info = () => void 0;
      const originalLogLevel = process.env[OM_LOG_LEVEL_ENV];
      process.env[OM_LOG_LEVEL_ENV] = "error";
      resetLogLevelCache();
      resetServerLoggerCache();
      restoreConsole = () => {
        console.log = originalLog;
        console.info = originalInfo;
        if (originalLogLevel === void 0) delete process.env[OM_LOG_LEVEL_ENV];
        else process.env[OM_LOG_LEVEL_ENV] = originalLogLevel;
        resetLogLevelCache();
        resetServerLoggerCache();
      };
    }
    const container = await createRequestContainer();
    const em = container.resolve("em");
    const roleNames = rolesCsv ? rolesCsv.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
    try {
      const modules = getCliModules();
      const result = await setupInitialTenant(em, {
        orgName,
        orgSlug,
        roleNames,
        primaryUser: { email, password, confirm: true },
        // Derived admin/employee demo accounts only land when the operator
        // explicitly opts in via --include-demo-users. Default-deny prevents
        // production deployments from silently seeding well-known emails.
        includeDerivedUsers: includeDemoUsers,
        // When demo users are explicitly requested, the operator has consented
        // to having admin@/employee@ accounts created with autogenerated
        // passwords (surfaced in stdout) when no env override is provided.
        allowDemoDerivedPasswords: includeDemoUsers,
        // When the caller passes an explicit slug, treat it as a "fresh tenant"
        // signal — silent reuse of an existing user's tenant defeats the point
        // of slugging the new tenant for downstream tooling.
        failIfUserExists: orgSlug !== void 0,
        modules
      });
      if (withExamples) {
        const seedCtx = {
          em,
          tenantId: result.tenantId,
          organizationId: result.organizationId,
          container
        };
        for (const mod of modules) {
          if (mod.setup?.seedExamples) {
            await mod.setup.seedExamples(seedCtx);
          }
        }
      }
      if (jsonMode) {
        const adminSnapshot = pickAdminSnapshot(result.users, email);
        const adminUserId = adminSnapshot && adminSnapshot.user.id != null ? String(adminSnapshot.user.id) : null;
        const payload = {
          tenantId: result.tenantId,
          organizationId: result.organizationId,
          adminUserId,
          adminEmail: email,
          reusedExistingUser: result.reusedExistingUser
        };
        if (restoreConsole) restoreConsole();
        process.stdout.write(`${JSON.stringify(payload)}
`);
        return;
      }
      if (result.reusedExistingUser) {
        console.log("\u26A0\uFE0F  Existing initial user detected during setup.");
        console.log(`\u26A0\uFE0F  Email: ${email}`);
        console.log("\u26A0\uFE0F  Updated roles if missing and reused tenant/organization.");
      }
      if (env.NODE_ENV !== "test") {
        for (const snapshot of result.users) {
          if (snapshot.created) {
            if (snapshot.generatedPassword) {
              console.log("\u26A0\uFE0F  GENERATED password \u2014 copy now; it is not stored in plain text");
              console.log("\u{1F389} Created user", snapshot.user.email, "password:", snapshot.generatedPassword);
            } else {
              console.log("\u{1F389} Created user", snapshot.user.email);
            }
          } else {
            console.log(`Updated user ${snapshot.user.email}`);
          }
        }
      }
      if (env.NODE_ENV !== "test") console.log("\u2705 Setup complete:", { tenantId: result.tenantId, organizationId: result.organizationId });
    } catch (err) {
      if (restoreConsole) restoreConsole();
      if (err instanceof OrgSlugExistsError) {
        process.stderr.write(`${err.message}
`);
        process.exitCode = 1;
        return;
      }
      if (err instanceof DerivedUserPasswordRequiredError) {
        process.stderr.write(
          `Setup aborted: ${err.message}. In production, set OM_INIT_ADMIN_PASSWORD and OM_INIT_EMPLOYEE_PASSWORD before passing --include-demo-users, or omit --include-demo-users to skip derived demo accounts entirely.
`
        );
        process.exitCode = 2;
        return;
      }
      if (err instanceof Error && err.message === "USER_EXISTS") {
        process.stderr.write("Setup aborted: user already exists with the provided email.\n");
        process.exitCode = 1;
        return;
      }
      throw err;
    } finally {
      if (restoreConsole) restoreConsole();
    }
  }
};
const ORG_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
function pickAdminSnapshot(users, adminEmail) {
  const normalizedEmail = adminEmail.toLowerCase();
  const exact = users.find(
    (snapshot) => snapshot.created && snapshot.roles.includes("superadmin") && typeof snapshot.user.email === "string" && snapshot.user.email.toLowerCase() === normalizedEmail
  );
  if (exact) return exact;
  return users.find((snapshot) => snapshot.roles.includes("superadmin"));
}
const listOrganizations = {
  command: "list-orgs",
  async run() {
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    const orgs = await findWithDecryption(
      em,
      Organization,
      {},
      { populate: ["tenant"] },
      { tenantId: null, organizationId: null }
    );
    if (orgs.length === 0) {
      console.log("No organizations found");
      return;
    }
    console.log(`Found ${orgs.length} organization(s):`);
    console.log("");
    console.log("ID                                   | Name                    | Tenant ID                            | Created");
    console.log("-------------------------------------|-------------------------|-------------------------------------|-------------------");
    for (const org of orgs) {
      const created = org.createdAt ? new Date(org.createdAt).toLocaleDateString() : "N/A";
      const id = org.id || "N/A";
      const tenantId = org.tenant?.id || "N/A";
      const name = (org.name || "Unnamed").padEnd(23);
      console.log(`${id.padEnd(35)} | ${name} | ${tenantId.padEnd(35)} | ${created}`);
    }
  }
};
const listTenants = {
  command: "list-tenants",
  async run() {
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    const tenants = await em.find(Tenant, {});
    if (tenants.length === 0) {
      console.log("No tenants found");
      return;
    }
    console.log(`Found ${tenants.length} tenant(s):`);
    console.log("");
    console.log("ID                                   | Name                    | Created");
    console.log("-------------------------------------|-------------------------|-------------------");
    for (const tenant of tenants) {
      const created = tenant.createdAt ? new Date(tenant.createdAt).toLocaleDateString() : "N/A";
      const id = tenant.id || "N/A";
      const name = (tenant.name || "Unnamed").padEnd(23);
      console.log(`${id.padEnd(35)} | ${name} | ${created}`);
    }
  }
};
const listUsers = {
  command: "list-users",
  async run(rest) {
    const args = {};
    for (let i = 0; i < rest.length; i += 2) {
      const k = rest[i]?.replace(/^--/, "");
      const v = rest[i + 1];
      if (k) args[k] = v;
    }
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    const where = {};
    if (args.organizationId || args.orgId || args.org) {
      where.organizationId = args.organizationId || args.orgId || args.org;
    }
    if (args.tenantId || args.tenant) {
      where.tenantId = args.tenantId || args.tenant;
    }
    const users = await em.find(User, where);
    if (users.length === 0) {
      console.log("No users found");
      return;
    }
    console.log(`Found ${users.length} user(s):`);
    console.log("");
    console.log("ID                                   | Email                   | Name                    | Organization ID      | Tenant ID            | Roles");
    console.log("-------------------------------------|-------------------------|-------------------------|---------------------|---------------------|-------------------");
    for (const user of users) {
      const userRoles = await findWithDecryption(
        em,
        UserRole,
        { user: user.id },
        { populate: ["role"] },
        { tenantId: user.tenantId ?? null, organizationId: user.organizationId ?? null }
      );
      const roles = userRoles.map((ur) => ur.role?.name).filter(Boolean).join(", ") || "None";
      let orgName = "N/A";
      let tenantName = "N/A";
      if (user.organizationId) {
        const org = await em.findOne(Organization, { id: user.organizationId });
        orgName = org?.name ? `${org.name.substring(0, 19)}...` : `${user.organizationId.substring(0, 8)}...`;
      }
      if (user.tenantId) {
        const tenant = await em.findOne(Tenant, { id: user.tenantId });
        tenantName = tenant?.name ? `${tenant.name.substring(0, 19)}...` : `${user.tenantId.substring(0, 8)}...`;
      }
      const id = user.id || "N/A";
      const email = (user.email || "N/A").padEnd(23);
      const name = (user.name || "Unnamed").padEnd(23);
      console.log(`${id.padEnd(35)} | ${email} | ${name} | ${orgName.padEnd(19)} | ${tenantName.padEnd(19)} | ${roles}`);
    }
  }
};
const setPassword = {
  command: "set-password",
  async run(rest) {
    const args = {};
    for (let i = 0; i < rest.length; i += 2) {
      const k = rest[i]?.replace(/^--/, "");
      const v = rest[i + 1];
      if (k) args[k] = v;
    }
    const email = args.email;
    const password = args.password;
    if (!email || !password) {
      console.error("Usage: mercato auth set-password --email <email> --password <newPassword>");
      return;
    }
    if (!ensurePasswordPolicy(password)) return;
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    const user = await findOneWithDecryption(
      em,
      User,
      { $or: [{ email }, { emailHash: { $in: emailHashLookupValues(email) } }] },
      void 0,
      { tenantId: null, organizationId: null }
    );
    if (!user) {
      console.error(`User with email "${email}" not found`);
      return;
    }
    user.passwordHash = await hash(password, 10);
    await em.persist(user).flush();
    console.log(`\u2705 Password updated successfully for user: ${email}`);
  }
};
const syncRoleAcls = {
  command: "sync-role-acls",
  async run(rest) {
    const args = {};
    const flags = /* @__PURE__ */ new Set();
    for (let i = 0; i < rest.length; i++) {
      const token = rest[i];
      if (!token?.startsWith("--")) continue;
      const key = token.replace(/^--/, "");
      const next = rest[i + 1];
      if (next && !next.startsWith("--")) {
        args[key] = next;
        i++;
      } else {
        flags.add(key);
      }
    }
    const tenantArg = args.tenantId ?? args.tenant ?? args.tenant_id ?? null;
    const includeSuperadminRole = !flags.has("no-superadmin");
    const modules = getCliModules();
    if (!modules.length) {
      console.error("\u274C No CLI modules registered. Run `yarn generate` first.");
      return;
    }
    const { resolve } = await createRequestContainer();
    const em = resolve("em");
    const targetTenantIds = [];
    if (tenantArg) {
      const normalized = normalizeTenantId(tenantArg);
      if (!normalized) {
        console.error(`\u274C Invalid --tenant value: ${tenantArg}`);
        return;
      }
      const tenant = await em.findOne(Tenant, { id: normalized });
      if (!tenant) {
        console.error(`\u274C Tenant not found: ${normalized}`);
        return;
      }
      targetTenantIds.push(normalized);
    } else {
      const tenants = await em.find(Tenant, {});
      if (!tenants.length) {
        console.log("No tenants found; nothing to sync.");
        return;
      }
      for (const tenant of tenants) {
        const id = tenant.id ? String(tenant.id) : null;
        if (id) targetTenantIds.push(id);
      }
    }
    for (const tenantId of targetTenantIds) {
      await ensureDefaultRoleAcls(em, tenantId, modules, { includeSuperadminRole });
      await ensureCustomRoleAcls(em, tenantId, modules);
      console.log(`\u2705 Synced role ACLs for tenant ${tenantId}`);
    }
  }
};
var cli_default = [addUser, seedRoles, syncRoleAcls, rotateEncryptionKey, addOrganization, setupApp, listOrganizations, listTenants, listUsers, setPassword];
export {
  cli_default as default
};
//# sourceMappingURL=cli.js.map
