import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
import { splitCustomFieldPayload } from "@open-mercato/shared/lib/crud/custom-fields";
const DEFAULT_MESSAGES = {
  tenantRequired: { key: "errors.tenant_required", fallback: "Tenant context is required." },
  organizationRequired: { key: "errors.organization_required", fallback: "Organization context is required." },
  idRequired: { key: "errors.id_required", fallback: "Record identifier is required." },
  tenantForbidden: { key: "errors.tenant_forbidden", fallback: "You are not allowed to target this tenant." }
};
function resolveMessage(messages, key) {
  const override = messages?.[key];
  if (override && typeof override.key === "string" && override.key.length > 0) {
    return {
      key: override.key,
      fallback: override.fallback ?? DEFAULT_MESSAGES[key].fallback
    };
  }
  return DEFAULT_MESSAGES[key];
}
function withScopedPayload(payload, ctx, translate, options = {}) {
  const requireOrganization = options.requireOrganization !== false;
  const source = payload ? { ...payload } : {};
  const tenantId = source?.tenantId ?? ctx.auth?.tenantId ?? null;
  if (!tenantId) {
    const msg = resolveMessage(options.messages, "tenantRequired");
    throw new CrudHttpError(400, { error: translate(msg.key, msg.fallback) });
  }
  const resolvedOrg = source?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null;
  if (requireOrganization && !resolvedOrg) {
    const msg = resolveMessage(options.messages, "organizationRequired");
    throw new CrudHttpError(400, { error: translate(msg.key, msg.fallback) });
  }
  const scoped = {
    ...source,
    tenantId
  };
  if (resolvedOrg) scoped.organizationId = resolvedOrg;
  return scoped;
}
function parseScopedCommandInput(schema, payload, ctx, translate, options = {}) {
  const scoped = withScopedPayload(
    payload && typeof payload === "object" ? payload : {},
    ctx,
    translate,
    options
  );
  const actorTenantId = normalizeTenant(ctx.auth?.tenantId);
  const requestedTenantId = normalizeTenant(scoped.tenantId);
  const isSuperAdmin = authIsSuperAdmin(ctx.auth);
  if (!isSuperAdmin) {
    if (actorTenantId) {
      if (!requestedTenantId || requestedTenantId !== actorTenantId) {
        const msg = resolveMessage(options.messages, "tenantForbidden");
        throw new CrudHttpError(403, { error: translate(msg.key, msg.fallback) });
      }
    } else if (requestedTenantId) {
      const msg = resolveMessage(options.messages, "tenantForbidden");
      throw new CrudHttpError(403, { error: translate(msg.key, msg.fallback) });
    }
  }
  const { base, custom } = splitCustomFieldPayload(scoped);
  const hasCustomFields = custom && Object.keys(custom).length > 0;
  const candidates = hasCustomFields ? [base, { ...base, customFields: custom }] : [base];
  let parsed;
  let lastError;
  for (const candidate of candidates) {
    try {
      parsed = schema.parse(candidate);
      break;
    } catch (err) {
      lastError = err;
    }
  }
  if (!parsed) {
    if (lastError instanceof Error) throw lastError;
    throw new CrudHttpError(400, { error: translate("errors.invalid_input", "Invalid input") });
  }
  const parsedWithCustom = hasCustomFields ? Object.assign({}, parsed, { customFields: custom }) : parsed;
  return parsedWithCustom;
}
function normalizeTenant(candidate) {
  if (typeof candidate === "string" && candidate.trim().length > 0) return candidate.trim();
  return null;
}
function authIsSuperAdmin(auth) {
  if (!auth) return false;
  return auth.isSuperAdmin === true;
}
function requireRecordId(candidate, ctx, translate, options = {}) {
  const fieldName = "id";
  const id = typeof candidate === "string" ? candidate.trim() : candidate && typeof candidate === "object" ? typeof candidate[fieldName] === "string" ? String(candidate[fieldName]) : null : null;
  if (id && id.length > 0) return id;
  const msg = resolveMessage(options.messages, "idRequired");
  throw new CrudHttpError(400, { error: translate(msg.key, msg.fallback) });
}
function resolveCrudRecordId(parsed, ctx, translate, options = {}) {
  const fieldName = options.fieldName ?? "id";
  const queryParam = options.queryParam ?? fieldName;
  const tryRequire = (value) => {
    try {
      return requireRecordId(value, ctx, translate, options);
    } catch {
      return null;
    }
  };
  if (parsed && typeof parsed === "object") {
    const body = parsed.body;
    const fromBody = body && typeof body === "object" ? tryRequire(body) : null;
    if (fromBody) return fromBody;
    const fallback = tryRequire(parsed);
    if (fallback) return fallback;
    const query = parsed.query;
    if (query && typeof query === "object") {
      const candidate = query[queryParam];
      if (typeof candidate === "string" && candidate.trim().length > 0) return candidate.trim();
    }
  }
  if (ctx.request instanceof Request) {
    const value = new URL(ctx.request.url).searchParams.get(queryParam);
    if (value && value.trim().length > 0) return value.trim();
  }
  const msg = resolveMessage(options.messages, "idRequired");
  throw new CrudHttpError(400, { error: translate(msg.key, msg.fallback) });
}
function createScopedApiHelpers(baseOptions) {
  return {
    withScopedPayload: (payload, ctx, translate, options = {}) => withScopedPayload(payload, ctx, translate, { ...baseOptions, ...options }),
    parseScopedCommandInput: (schema, payload, ctx, translate, options = {}) => parseScopedCommandInput(schema, payload, ctx, translate, { ...baseOptions, ...options }),
    requireRecordId: (candidate, ctx, translate, options = {}) => requireRecordId(candidate, ctx, translate, { ...baseOptions, ...options }),
    resolveCrudRecordId: (parsed, ctx, translate, options = {}) => resolveCrudRecordId(parsed, ctx, translate, { ...baseOptions, ...options })
  };
}
export {
  createScopedApiHelpers,
  parseScopedCommandInput,
  requireRecordId,
  resolveCrudRecordId,
  withScopedPayload
};
//# sourceMappingURL=scoped.js.map
