(function attachGoPayUtils(root, factory) {
  root.GoPayUtils = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createGoPayUtils() {
  const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
  const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
  const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
  const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top';
  const GPC_HELPER_PHONE_MODE_AUTO = 'auto';
  const GPC_HELPER_PHONE_MODE_MANUAL = 'manual';
  const ALLOWED_GPC_HELPER_REMOTE_HOST = 'gpc.qlhazycoder.top';

  function normalizePlusPaymentMethod(value = '') {
    const normalized = String(value || '').trim().toLowerCase();
    if (normalized === PLUS_PAYMENT_METHOD_GPC_HELPER) {
      return PLUS_PAYMENT_METHOD_GPC_HELPER;
    }
    return normalized === PLUS_PAYMENT_METHOD_GOPAY ? PLUS_PAYMENT_METHOD_GOPAY : PLUS_PAYMENT_METHOD_PAYPAL;
  }

  const DEFAULT_GOPAY_COUNTRY_CODE = '+86';

  function normalizeGoPayCountryCode(value = '') {
    const normalized = String(value || '').trim().replace(/[^\d+]/g, '');
    const digits = normalized.replace(/\D/g, '');
    return digits ? `+${digits}` : DEFAULT_GOPAY_COUNTRY_CODE;
  }

  function normalizeGoPayPhone(value = '') {
    return String(value || '').trim().replace(/[^\d+]/g, '');
  }

  function normalizeGoPayPhoneForCountry(value = '', countryCode = DEFAULT_GOPAY_COUNTRY_CODE) {
    const normalizedPhone = normalizeGoPayPhone(value);
    const normalizedCountryCode = normalizeGoPayCountryCode(countryCode);
    const countryDigits = normalizedCountryCode.replace(/\D/g, '');
    let nationalNumber = normalizedPhone.replace(/\D/g, '');

    if (countryDigits && nationalNumber.startsWith(countryDigits)) {
      nationalNumber = nationalNumber.slice(countryDigits.length);
    }
    return nationalNumber;
  }

  function normalizeGoPayPin(value = '') {
    return String(value || '').trim().replace(/[^\d]/g, '');
  }

  function normalizeGoPayOtp(value = '') {
    return String(value || '').trim().replace(/[^\d]/g, '');
  }

  function normalizeGpcHelperPhoneMode(value = '') {
    const normalized = String(value || '').trim().toLowerCase();
    return normalized === GPC_HELPER_PHONE_MODE_AUTO || normalized === 'builtin'
      ? GPC_HELPER_PHONE_MODE_AUTO
      : GPC_HELPER_PHONE_MODE_MANUAL;
  }

  function normalizeGpcRemainingUses(value) {
    if (value === undefined || value === null || String(value).trim() === '') {
      return null;
    }
    const numeric = Number(value);
    return Number.isFinite(numeric) ? Math.max(0, Math.floor(numeric)) : null;
  }

  function unwrapGpcBalancePayload(payload = {}) {
    const data = unwrapGpcResponse(payload);
    if (!data || typeof data !== 'object' || Array.isArray(data)) {
      return data;
    }
    const hasBalanceFields = [
      'remaining_uses',
      'remainingUses',
      'balance',
      'remaining',
      'uses',
      'available_uses',
      'availableUses',
      'auto_mode_enabled',
      'autoModeEnabled',
      'auto_enabled',
      'autoEnabled',
      'status',
      'card_status',
      'cardStatus',
    ].some((key) => Object.prototype.hasOwnProperty.call(data, key));
    if (!hasBalanceFields && data.data && typeof data.data === 'object' && !Array.isArray(data.data)) {
      return data.data;
    }
    return data;
  }

  function getGpcBalanceRemainingUses(payload = {}) {
    const data = unwrapGpcBalancePayload(payload);
    if (!data || typeof data !== 'object') {
      return null;
    }
    return normalizeGpcRemainingUses(
      data.remaining_uses
      ?? data.remainingUses
      ?? data.balance
      ?? data.remaining
      ?? data.uses
      ?? data.available_uses
      ?? data.availableUses
    );
  }

  function isGpcAutoModeEnabled(payload = {}) {
    const data = unwrapGpcBalancePayload(payload);
    if (!data || typeof data !== 'object') {
      return false;
    }
    const raw = data.auto_mode_enabled ?? data.autoModeEnabled ?? data.auto_enabled ?? data.autoEnabled;
    if (typeof raw === 'boolean') {
      return raw;
    }
    const normalized = String(raw ?? '').trim().toLowerCase();
    return normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'enabled';
  }

  function getGpcApiKeyStatus(payload = {}) {
    const data = unwrapGpcBalancePayload(payload);
    if (!data || typeof data !== 'object') {
      return '';
    }
    return String(data.status || data.card_status || data.cardStatus || '').trim();
  }

  function normalizeGpcOtpChannel(value = '') {
    const normalized = String(value || '').trim().toLowerCase();
    if (normalized === 'sms') {
      return 'sms';
    }
    return 'whatsapp';
  }

  function normalizeGpcHelperBaseUrl(apiUrl = '') {
    let normalized = String(apiUrl || DEFAULT_GPC_HELPER_API_URL).trim();
    if (!normalized) {
      return DEFAULT_GPC_HELPER_API_URL;
    }
    normalized = normalized.replace(/\/+$/g, '');
    normalized = normalized.replace(/\/api\/checkout\/start$/i, '');
    normalized = normalized.replace(/\/api\/gopay\/(?:otp|pin)$/i, '');
    normalized = normalized.replace(/\/api\/gp\/tasks(?:\/[^/?#]+)?(?:\/(?:otp|pin|stop))?(?:\?.*)?$/i, '');
    normalized = normalized.replace(/\/api\/gp\/balance(?:\?.*)?$/i, '');
    normalized = normalized.replace(/\/api\/card\/balance(?:\?.*)?$/i, '');
    normalized = normalized.replace(/\/api\/card\/redeem-api-key(?:\?.*)?$/i, '');

    try {
      const parsed = new URL(normalized);
      const hostname = parsed.hostname.toLowerCase();
      if (hostname === ALLOWED_GPC_HELPER_REMOTE_HOST || hostname === 'localhost' || hostname === '127.0.0.1') {
        return normalized || DEFAULT_GPC_HELPER_API_URL;
      }
      return DEFAULT_GPC_HELPER_API_URL;
    } catch {
      return DEFAULT_GPC_HELPER_API_URL;
    }
  }

  function buildGpcHelperApiUrl(apiUrl = '', path = '') {
    const baseUrl = normalizeGpcHelperBaseUrl(apiUrl);
    if (!baseUrl) {
      return '';
    }
    const normalizedPath = String(path || '').startsWith('/') ? String(path || '') : `/${String(path || '')}`;
    return `${baseUrl}${normalizedPath}`;
  }

  function buildGpcApiKeyBalanceUrl(apiUrl = '') {
    return buildGpcHelperApiUrl(apiUrl, '/api/gp/balance');
  }

  function buildGpcCardBalanceUrl(apiUrl = '') {
    return buildGpcApiKeyBalanceUrl(apiUrl);
  }

  function buildGpcApiKeyHeaders(apiKey = '', extraHeaders = {}) {
    const headers = {
      ...(extraHeaders && typeof extraHeaders === 'object' ? extraHeaders : {}),
    };
    const normalizedApiKey = String(apiKey || '').trim();
    if (normalizedApiKey) {
      headers['X-API-Key'] = normalizedApiKey;
    }
    return headers;
  }

  function buildGpcTaskCreateUrl(apiUrl = '') {
    return buildGpcHelperApiUrl(apiUrl, '/api/gp/tasks');
  }

  function normalizeGpcTaskId(value = '') {
    return String(value || '').trim();
  }

  function buildGpcTaskQueryUrl(apiUrl = '', taskId = '') {
    const normalizedTaskId = normalizeGpcTaskId(taskId);
    return buildGpcHelperApiUrl(apiUrl, `/api/gp/tasks/${encodeURIComponent(normalizedTaskId)}`);
  }

  function buildGpcTaskActionUrl(apiUrl = '', taskId = '', action = '') {
    const normalizedTaskId = normalizeGpcTaskId(taskId);
    const normalizedAction = String(action || '').trim().replace(/^\/+|\/+$/g, '');
    return buildGpcHelperApiUrl(apiUrl, `/api/gp/tasks/${encodeURIComponent(normalizedTaskId)}/${normalizedAction}`);
  }

  function unwrapGpcResponse(payload = {}) {
    if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
      return payload;
    }
    const hasUnifiedShape = Object.prototype.hasOwnProperty.call(payload, 'data')
      && (
        Object.prototype.hasOwnProperty.call(payload, 'code')
        || Object.prototype.hasOwnProperty.call(payload, 'message')
      );
    return hasUnifiedShape ? (payload.data ?? {}) : payload;
  }

  function isGpcUnifiedResponseOk(payload = {}) {
    if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
      return true;
    }
    if (!Object.prototype.hasOwnProperty.call(payload, 'code')) {
      return payload.ok !== false;
    }
    const code = Number(payload.code);
    if (Number.isFinite(code)) {
      return code >= 200 && code < 300;
    }
    return String(payload.code || '').trim() === '200';
  }

  function formatGpcErrorField(field) {
    if (field === undefined || field === null) {
      return '';
    }
    if (typeof field === 'string') {
      return field.trim();
    }
    if (typeof field !== 'object') {
      return String(field).trim();
    }
    const key = Array.isArray(field.loc)
      ? field.loc.join('.')
      : String(field.field || field.path || field.name || field.param || '').trim();
    const message = String(field.msg || field.message || field.error || field.detail || field.reason || '').trim();
    return [key, message].filter(Boolean).join(': ') || JSON.stringify(field);
  }

  function extractGpcResponseErrorDetail(payload = {}, status = 0) {
    if (!payload || typeof payload !== 'object') {
      return status ? `HTTP ${status}` : '未知错误';
    }

    const payloadText = JSON.stringify(payload).toLowerCase();
    if (/account\s+already\s+linked/i.test(payloadText)) {
      return 'GOPAY已经绑了订阅，需要手动解绑';
    }

    const data = payload.data;
    if (data && typeof data === 'object' && !Array.isArray(data)) {
      const nestedDetail = data.detail ?? data.error ?? data.reason;
      if (nestedDetail !== undefined && nestedDetail !== null && String(nestedDetail).trim()) {
        const nestedText = String(nestedDetail).trim();
        return /account\s+already\s+linked/i.test(nestedText)
          ? 'GOPAY已经绑了订阅，需要手动解绑'
          : nestedText;
      }
      const fields = data.fields ?? data.errors;
      if (Array.isArray(fields) && fields.length > 0) {
        const formatted = fields
          .map(formatGpcErrorField)
          .filter(Boolean)
          .join('; ');
        if (formatted) {
          return formatted;
        }
      }
    }

    const direct = payload.detail
      ?? payload.message
      ?? payload.error
      ?? payload.error_description
      ?? payload.reason;
    if (direct !== undefined && direct !== null && String(direct).trim()) {
      const directText = String(direct).trim();
      return /account\s+already\s+linked/i.test(directText)
        ? 'GOPAY已经绑了订阅，需要手动解绑'
        : directText;
    }

    const errorMessages = payload.error_messages ?? payload.errorMessages;
    if (Array.isArray(errorMessages) && errorMessages.length > 0) {
      const firstMessage = String(errorMessages[0] || '').trim();
      if (/account\s+already\s+linked/i.test(firstMessage)) {
        return 'GOPAY已经绑了订阅，需要手动解绑';
      }
      if (firstMessage) {
        return firstMessage;
      }
    }

    const errors = payload.errors;
    if (Array.isArray(errors) && errors.length > 0) {
      const first = errors[0];
      if (typeof first === 'string') {
        return first.trim() || (status ? `HTTP ${status}` : '未知错误');
      }
      if (first && typeof first === 'object') {
        const field = Array.isArray(first.loc) ? first.loc.join('.') : String(first.field || first.path || '').trim();
        const message = String(first.msg || first.message || first.error || '').trim();
        return [field, message].filter(Boolean).join(': ') || JSON.stringify(first);
      }
    }

    return status ? `HTTP ${status}` : '未知错误';
  }

  function buildGpcOtpPayload(input = {}) {
    const payload = {
      reference_id: String(input.reference_id ?? input.referenceId ?? '').trim(),
      otp: normalizeGoPayOtp(input.otp ?? input.code ?? ''),
    };
    const flowId = String(input.flow_id ?? input.flowId ?? '').trim();
    const gopayGuid = String(input.gopay_guid ?? input.gopayGuid ?? '').trim();
    const redirectUrl = String(input.redirect_url ?? input.redirectUrl ?? '').trim();
    if (flowId) payload.flow_id = flowId;
    if (gopayGuid) payload.gopay_guid = gopayGuid;
    if (redirectUrl) payload.redirect_url = redirectUrl;
    return payload;
  }

  function buildGpcOtpRetryPayload(input = {}) {
    const payload = buildGpcOtpPayload(input);
    return { ...payload, code: payload.otp };
  }

  function buildGpcPinPayload(input = {}) {
    const payload = {
      reference_id: String(input.reference_id ?? input.referenceId ?? '').trim(),
      challenge_id: String(input.challenge_id ?? input.challengeId ?? '').trim(),
      gopay_guid: String(input.gopay_guid ?? input.gopayGuid ?? '').trim(),
      pin: normalizeGoPayPin(input.pin ?? ''),
    };
    const flowId = String(input.flow_id ?? input.flowId ?? '').trim();
    const redirectUrl = String(input.redirect_url ?? input.redirectUrl ?? '').trim();
    if (flowId) payload.flow_id = flowId;
    if (redirectUrl) payload.redirect_url = redirectUrl;
    return payload;
  }

  function buildGpcPinRetryPayload(input = {}) {
    const payload = buildGpcPinPayload(input);
    return { ...payload, challengeId: payload.challenge_id };
  }

  function buildGpcTaskOtpPayload(input = {}) {
    return {
      otp: normalizeGoPayOtp(input.otp ?? input.code ?? ''),
    };
  }

  function buildGpcTaskPinPayload(input = {}) {
    return {
      pin: normalizeGoPayPin(input.pin ?? ''),
    };
  }

  function formatGpcBalancePayload(payload = {}) {
    const data = unwrapGpcBalancePayload(payload);
    if (!data || typeof data !== 'object') {
      return '';
    }
    const candidates = [
      data.remaining_uses,
      data.remainingUses,
      data.balance,
      data.remaining,
      data.uses,
      data.available_uses,
      data.availableUses,
    ];
    const firstValue = candidates.find((value) => value !== undefined && value !== null && String(value).trim() !== '');
    const totalUses = data.total_uses ?? data.totalUses;
    const usedUses = data.used_uses ?? data.usedUses;
    const status = String(data.status || data.card_status || data.cardStatus || '').trim();
    const flowId = String(data.flow_id || data.flowId || '').trim();
    const parts = [];
    if (firstValue !== undefined) {
      parts.push(totalUses !== undefined && totalUses !== null && String(totalUses).trim() !== ''
        ? `余额 ${firstValue}/${totalUses}`
        : `余额 ${firstValue}`);
    }
    if (usedUses !== undefined && usedUses !== null && String(usedUses).trim() !== '') {
      parts.push(`已用 ${usedUses}`);
    }
    if (status) {
      parts.push(`状态 ${status}`);
    }
    if (flowId) {
      parts.push(`flow_id ${flowId}`);
    }
    return parts.join('，');
  }

  return {
    DEFAULT_GOPAY_COUNTRY_CODE,
    DEFAULT_GPC_HELPER_API_URL,
    GPC_HELPER_PHONE_MODE_AUTO,
    GPC_HELPER_PHONE_MODE_MANUAL,
    PLUS_PAYMENT_METHOD_GPC_HELPER,
    PLUS_PAYMENT_METHOD_GOPAY,
    PLUS_PAYMENT_METHOD_PAYPAL,
    buildGpcCardBalanceUrl,
    buildGpcApiKeyBalanceUrl,
    buildGpcApiKeyHeaders,
    buildGpcHelperApiUrl,
    buildGpcOtpPayload,
    buildGpcOtpRetryPayload,
    buildGpcPinPayload,
    buildGpcPinRetryPayload,
    buildGpcTaskActionUrl,
    buildGpcTaskCreateUrl,
    buildGpcTaskOtpPayload,
    buildGpcTaskPinPayload,
    buildGpcTaskQueryUrl,
    extractGpcResponseErrorDetail,
    formatGpcBalancePayload,
    getGpcApiKeyStatus,
    getGpcBalanceRemainingUses,
    isGpcUnifiedResponseOk,
    isGpcAutoModeEnabled,
    normalizeGpcHelperBaseUrl,
    normalizeGpcHelperPhoneMode,
    normalizeGpcRemainingUses,
    normalizeGpcTaskId,
    normalizeGoPayCountryCode,
    normalizeGoPayPhone,
    normalizeGoPayPhoneForCountry,
    normalizeGoPayOtp,
    normalizeGoPayPin,
    normalizeGpcOtpChannel,
    normalizePlusPaymentMethod,
    unwrapGpcBalancePayload,
    unwrapGpcResponse,
  };
});
