'use strict';

// src/errors.ts
var NeetruError = class _NeetruError extends Error {
  code;
  status;
  requestId;
  constructor(code, message, status, requestId) {
    super(message);
    this.name = "NeetruError";
    this.code = code;
    this.status = status;
    this.requestId = requestId;
    Object.setPrototypeOf(this, _NeetruError.prototype);
  }
};

// src/idempotency.ts
function generateIdempotencyKey() {
  const cryptoObj = typeof globalThis !== "undefined" && "crypto" in globalThis ? globalThis.crypto : void 0;
  if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
    return cryptoObj.randomUUID();
  }
  throw new NeetruError(
    "runtime_unsupported",
    "crypto.randomUUID not available in this runtime. SDK 3.0 requires Node \u226520 or modern browser/Edge."
  );
}

// src/http.ts
var COMPAT_MATRIX = {
  // SDK major 3.x requer Core ≥ 2026.05.1 (suporte a X-Neetru-Version header,
  // runTransaction write-after-read, verifyWebhookRequest, webhooks 3.0+)
  3: "2026.05.1"
};
function stripRevision(v) {
  return v.split("+")[0] ?? v;
}
function compareCalVer(a, b) {
  const partsA = stripRevision(a).split(".").map(Number);
  const partsB = stripRevision(b).split(".").map(Number);
  const len = Math.max(partsA.length, partsB.length);
  for (let i = 0; i < len; i++) {
    const pa = partsA[i] ?? 0;
    const pb = partsB[i] ?? 0;
    if (pa !== pb) return pa - pb;
  }
  return 0;
}
function getSdkMajor() {
  try {
    const ver = globalThis.__NEETRU_SDK_VERSION__;
    if (ver) return parseInt(ver.split(".")[0] ?? "3", 10);
  } catch {
  }
  return 3;
}
var _warnedCoreTooOld = /* @__PURE__ */ new Set();
function checkCoreVersionCompat(coreVersion) {
  if (!coreVersion) return;
  const sdkMajor = getSdkMajor();
  const minCore = COMPAT_MATRIX[sdkMajor];
  if (!minCore) return;
  const stripped = stripRevision(coreVersion);
  if (compareCalVer(stripped, minCore) < 0) {
    const key = `${sdkMajor}:${stripped}`;
    if (!_warnedCoreTooOld.has(key)) {
      _warnedCoreTooOld.add(key);
      console.warn(
        `[Neetru SDK] Aten\xE7\xE3o: Core vers\xE3o "${coreVersion}" \xE9 anterior ao m\xEDnimo recomendado "${minCore}" para SDK major ${sdkMajor}. Alguns recursos podem n\xE3o funcionar corretamente. Atualize o Core para a vers\xE3o mais recente.`
      );
    }
  }
}
var DEFAULT_RETRIES_READ = 2;
var DEFAULT_RETRIES_WRITE = 0;
var READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
var RETRYABLE_CODES = /* @__PURE__ */ new Set([
  "rate_limited",
  "server_error",
  "network_error"
]);
function backoffMs(attempt) {
  const base = 200 * Math.pow(4, attempt);
  const jitter = base * 0.2 * (Math.random() * 2 - 1);
  return Math.max(50, Math.round(base + jitter));
}
var MAX_RETRY_AFTER_MS = 6e4;
function parseRetryAfter(value) {
  if (!value) return null;
  const secs = Number(value);
  if (Number.isFinite(secs) && secs >= 0) return Math.min(Math.round(secs * 1e3), MAX_RETRY_AFTER_MS);
  const dateMs = Date.parse(value);
  if (Number.isFinite(dateMs)) {
    const delta = dateMs - Date.now();
    if (delta > 0) return Math.min(delta, MAX_RETRY_AFTER_MS);
  }
  return null;
}
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
function statusToCode(status) {
  if (status === 401) return "unauthorized";
  if (status === 403) return "forbidden";
  if (status === 404) return "not_found";
  if (status === 409) return "conflict";
  if (status === 422 || status === 400) return "validation_failed";
  if (status === 429) return "rate_limited";
  if (status >= 500) return "server_error";
  return "unknown";
}
function buildUrl(baseUrl, path, query) {
  const base = baseUrl.replace(/\/+$/, "");
  const p = path.startsWith("/") ? path : `/${path}`;
  const url = new URL(`${base}${p}`);
  if (query) {
    for (const [k, v] of Object.entries(query)) {
      if (v === void 0) continue;
      url.searchParams.set(k, String(v));
    }
  }
  return url.toString();
}
async function safeJson(res) {
  const text = await res.text();
  if (!text) return void 0;
  try {
    return JSON.parse(text);
  } catch {
    return void 0;
  }
}
async function httpRequest(config, opts) {
  const method = opts.method ?? "GET";
  const url = buildUrl(config.baseUrl, opts.path, opts.query);
  const defaultRetries = READ_METHODS.has(method) ? DEFAULT_RETRIES_READ : DEFAULT_RETRIES_WRITE;
  const maxRetries = opts.retries ?? defaultRetries;
  const headers = {
    accept: "application/json",
    ...opts.headers
  };
  if (opts.requireAuth) {
    if (!config.apiKey) {
      throw new NeetruError(
        "missing_api_key",
        "This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var."
      );
    }
    headers.authorization = `Bearer ${config.apiKey}`;
  }
  if (opts.idempotencyKey) {
    const key = typeof opts.idempotencyKey === "string" ? opts.idempotencyKey : generateIdempotencyKey();
    headers["idempotency-key"] = key;
  }
  const bodyString = opts.body !== void 0 && method !== "GET" && method !== "DELETE" ? JSON.stringify(opts.body) : void 0;
  if (bodyString !== void 0) {
    headers["content-type"] = "application/json";
  }
  let lastError = null;
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const init = { method, headers };
    if (bodyString !== void 0) init.body = bodyString;
    init.signal = AbortSignal.timeout(3e4);
    let res;
    try {
      res = await config.fetch(url, init);
    } catch (err2) {
      const message2 = err2 instanceof DOMException && err2.name === "TimeoutError" ? "Network error: timeout after 30s" : `Network error: ${err2 instanceof Error ? err2.message : "fetch failed"}`;
      lastError = new NeetruError("network_error", message2);
      if (attempt < maxRetries) {
        await sleep(backoffMs(attempt));
        continue;
      }
      throw lastError;
    }
    const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
    if (res.ok) {
      const coreVersion = res.headers.get("x-neetru-version");
      if (coreVersion) checkCoreVersionCompat(coreVersion);
      const parsed = await safeJson(res);
      return parsed;
    }
    const body = await safeJson(res);
    let code = statusToCode(res.status);
    let message = `HTTP ${res.status}`;
    if (body && typeof body === "object" && "error" in body) {
      const errField = body.error;
      if (typeof errField === "string") {
        message = errField;
      } else if (errField && typeof errField === "object") {
        if (typeof errField.code === "string") code = errField.code;
        if (typeof errField.message === "string") message = errField.message;
      }
    }
    const err = new NeetruError(code, message, res.status, requestId);
    lastError = err;
    const isRetryable = RETRYABLE_CODES.has(code);
    if (isRetryable && attempt < maxRetries) {
      const retryAfter = parseRetryAfter(res.headers.get("retry-after"));
      const delay = retryAfter ?? backoffMs(attempt);
      await sleep(delay);
      continue;
    }
    throw err;
  }
  throw lastError ?? new NeetruError("unknown", "unexpected httpRequest exit");
}

// src/checkout.ts
function checkoutAuth(accessToken, method) {
  if (!accessToken || typeof accessToken !== "string") {
    throw new NeetruError(
      "validation_failed",
      `checkout.${method} requer o access_token OIDC do usu\xE1rio final (passe \`accessToken\`). A chave de API do SDK (nrt_*) N\xC3O \xE9 aceita pelo endpoint de checkout \u2014 ele autentica a identidade do cliente que est\xE1 assinando (Bearer access_token OIDC ou cookie de sess\xE3o).`
    );
  }
  return { requireAuth: false, headers: { authorization: `Bearer ${accessToken}` } };
}
function parseStartResponse(raw) {
  if (!raw || typeof raw !== "object") {
    throw new NeetruError("invalid_response", "checkout.start response is not an object");
  }
  const r = raw;
  if (typeof r.intentId !== "string" || !r.intentId) {
    throw new NeetruError("invalid_response", "checkout.start response missing intentId");
  }
  if (typeof r.redirectUrl !== "string" || !r.redirectUrl) {
    throw new NeetruError("invalid_response", "checkout.start response missing redirectUrl");
  }
  return {
    intentId: r.intentId,
    redirectUrl: r.redirectUrl,
    status: r.status ?? "pending",
    expiresAt: typeof r.expiresAt === "string" ? r.expiresAt : (/* @__PURE__ */ new Date()).toISOString(),
    requiresKyc: r.requiresKyc === true
  };
}
function parseGetResponse(raw) {
  if (!raw || typeof raw !== "object") {
    throw new NeetruError("invalid_response", "checkout.get response is not an object");
  }
  const r = raw;
  const intent = r.intent;
  if (!intent || typeof intent !== "object") {
    throw new NeetruError("invalid_response", "checkout.get response missing intent");
  }
  if (typeof intent.intentId !== "string") {
    throw new NeetruError("invalid_response", "checkout.get response missing intentId");
  }
  return {
    intentId: intent.intentId,
    uid: intent.uid ?? "",
    targetTenantId: intent.targetTenantId ?? "",
    targetTenantType: intent.targetTenantType ?? "pf",
    productId: intent.productId ?? "",
    planId: intent.planId ?? "",
    callbackUrl: intent.callbackUrl ?? "",
    status: intent.status ?? "pending",
    stripeSessionId: intent.stripeSessionId ?? null,
    expiresAt: intent.expiresAt ?? (/* @__PURE__ */ new Date()).toISOString(),
    isStale: r.isStale === true
  };
}
function inBrowser() {
  try {
    return typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined" && typeof globalThis.location !== "undefined" && typeof globalThis.location.assign === "function";
  } catch {
    return false;
  }
}
function performRedirect(url) {
  try {
    globalThis.location.assign(url);
  } catch {
  }
}
function createHttpCheckoutNamespace(config) {
  return {
    async start(input) {
      if (!input?.productId) {
        throw new NeetruError("validation_failed", "checkout.start: productId is required");
      }
      if (!input?.planId) {
        throw new NeetruError("validation_failed", "checkout.start: planId is required");
      }
      if (!input?.callbackUrl) {
        throw new NeetruError("validation_failed", "checkout.start: callbackUrl is required");
      }
      const body = {
        productId: input.productId,
        planId: input.planId,
        callbackUrl: input.callbackUrl
      };
      if (input.tenantType) body.targetTenantType = input.tenantType;
      if (input.tenantId) body.targetTenantId = input.tenantId;
      const { requireAuth, headers } = checkoutAuth(input.accessToken, "start");
      const raw = await httpRequest(config, {
        method: "POST",
        path: "/api/v1/checkout/intents",
        body,
        requireAuth,
        headers,
        idempotencyKey: true
      });
      const result = parseStartResponse(raw);
      const shouldRedirect = input.autoRedirect !== false;
      if (shouldRedirect && inBrowser()) {
        performRedirect(result.redirectUrl);
      }
      return result;
    },
    async get(intentId, opts) {
      if (!intentId || typeof intentId !== "string") {
        throw new NeetruError("validation_failed", "checkout.get: intentId is required");
      }
      const { requireAuth, headers } = checkoutAuth(opts?.accessToken, "get");
      const raw = await httpRequest(config, {
        method: "GET",
        path: `/api/v1/checkout/intents/${encodeURIComponent(intentId)}`,
        requireAuth,
        headers
      });
      return parseGetResponse(raw);
    },
    async cancel(intentId, opts) {
      if (!intentId || typeof intentId !== "string") {
        throw new NeetruError("validation_failed", "checkout.cancel: intentId is required");
      }
      const { requireAuth, headers } = checkoutAuth(opts?.accessToken, "cancel");
      const raw = await httpRequest(config, {
        method: "DELETE",
        path: `/api/v1/checkout/intents/${encodeURIComponent(intentId)}`,
        requireAuth,
        headers
      });
      return {
        ok: true,
        alreadyCancelled: raw?.alreadyCancelled === true
      };
    }
  };
}
var MockCheckout = class {
  intents = /* @__PURE__ */ new Map();
  async start(input) {
    if (!input?.productId) {
      throw new NeetruError("validation_failed", "checkout.start: productId is required");
    }
    if (!input?.planId) {
      throw new NeetruError("validation_failed", "checkout.start: planId is required");
    }
    if (!input?.callbackUrl) {
      throw new NeetruError("validation_failed", "checkout.start: callbackUrl is required");
    }
    const intentId = `chk_mock_${Math.random().toString(36).slice(2, 10)}`;
    const expiresAt = new Date(Date.now() + 15 * 60 * 1e3).toISOString();
    const redirectUrl = `https://localhost:9003/portal/checkout/${intentId}`;
    this.intents.set(intentId, {
      intentId,
      uid: "dev-fixture-uid",
      targetTenantId: input.tenantId ?? "dev-fixture-uid",
      targetTenantType: input.tenantType ?? "pf",
      productId: input.productId,
      planId: input.planId,
      callbackUrl: input.callbackUrl,
      status: "pending",
      stripeSessionId: null,
      expiresAt
    });
    return {
      intentId,
      redirectUrl,
      status: "pending",
      expiresAt,
      requiresKyc: false
    };
  }
  async get(intentId, _opts) {
    const found = this.intents.get(intentId);
    if (!found) {
      throw new NeetruError("not_found", `Mock intent ${intentId} not found`);
    }
    return { ...found };
  }
  async cancel(intentId, _opts) {
    const found = this.intents.get(intentId);
    if (!found) {
      throw new NeetruError("not_found", `Mock intent ${intentId} not found`);
    }
    const alreadyCancelled = found.status === "cancelled";
    this.intents.set(intentId, { ...found, status: "cancelled" });
    return { ok: true, alreadyCancelled };
  }
};
function createCheckoutNamespace(config) {
  if (config.env === "dev") return new MockCheckout();
  return createHttpCheckoutNamespace(config);
}

exports.MockCheckout = MockCheckout;
exports.createCheckoutNamespace = createCheckoutNamespace;
//# sourceMappingURL=checkout.cjs.map
//# sourceMappingURL=checkout.cjs.map