// Map transport/bridge errors to localized human copy. The connector ships
// stable errcodes for every login failure (pkg/connector/login.go:46-82 —
// FI.MAU.WHATSAPP.*), so unlike the Telegram widget there is no need for
// substring matching against upstream error text; errcodes are authoritative.

import { ProvisioningError } from './provisioning';
import type { T } from './i18n';

export const describeApiError = (err: unknown, t: T): string => {
  if (err instanceof ProvisioningError) {
    switch (err.errcode) {
      // Bad phone input (whatsmeow validation, login.go:200-203). The
      // connector kills the login process on these — the form transparently
      // restarts on resubmit.
      case 'FI.MAU.WHATSAPP.PHONE_NUMBER_TOO_SHORT':
      case 'FI.MAU.WHATSAPP.PHONE_NUMBER_NOT_INTERNATIONAL':
        return t('error.phone-invalid');
      // whatsmeow ErrIQRateOverLimit while requesting a pairing code
      // (login.go:204-205) — WhatsApp throttles repeat requests hard.
      case 'FI.MAU.WHATSAPP.RATE_LIMITED':
        return t('error.rate-limited');
      // QR codes ran out (~160 s window) or the pairing socket dropped
      // (login.go:275-277).
      case 'FI.MAU.WHATSAPP.LOGIN_TIMEOUT':
        return t('error.login-timeout');
      // QR scanned from a WhatsApp account without multidevice
      // (login.go:259-261).
      case 'FI.MAU.WHATSAPP.MULTIDEVICE_NOT_ENABLED':
        return t('error.multidevice');
      // whatsmeow is too old for WhatsApp's servers — operator-side problem
      // (login.go:262-264).
      case 'FI.MAU.WHATSAPP.CLIENT_OUTDATED':
        return t('error.client-outdated');
      // Phone-side pair rejection / unexpected socket event
      // (login.go:268-280).
      case 'FI.MAU.WHATSAPP.PAIR_ERROR':
      case 'FI.MAU.WHATSAPP.LOGIN_UNEXPECTED_EVENT':
        return t('error.pair-failed');
      case 'FI.MAU.BRIDGE.TOO_MANY_LOGINS':
        return t('error.too-many-logins');
      // Framework-level login lifecycle errors (mautrix ≥ v0.28.1
      // provisioninglogin.go — the server-side 30-minute login TTL).
      case 'FI.MAU.BRIDGE.LOGIN_TIMED_OUT':
        return t('error.login-timeout');
      case 'FI.MAU.BRIDGE.LOGIN_CANCELLED':
        return t('error.login-restart');
      // The login step machine advanced without us (a re-poll raced a state
      // change) — the process is desynced beyond recovery; start over.
      case 'M_BAD_STATE':
        return t('error.login-restart');
      // Identifier probe rejected as non-phone (startchat.go:52 — WhatsApp
      // only resolves phone numbers).
      case 'M_INVALID_PARAM':
        return t('error.phone-invalid');
      case 'M_MISSING_TOKEN':
      case 'M_UNKNOWN_TOKEN':
        return t('error.auth');
      case 'M_FORBIDDEN':
        // The connector reuses M_FORBIDDEN for «must be logged in to list
        // contacts» (startchat.go:184) — e.g. the session was severed from
        // the phone while the contacts view is open. That's a relink
        // problem, not an OpenID one.
        return err.message.toLowerCase().includes('logged in')
          ? t('error.not-logged-in')
          : t('error.auth');
      default:
        break;
    }
    // Login process evaporated server-side (the bridge deletes it on any
    // step error and when the QR window closes) — start over.
    if (err.httpStatus === 404) return t('error.login-restart');
    return t('error.generic', { reason: err.message });
  }
  // Host refused to issue OpenID creds — an operator-side config gap
  // (missing `vojo.openid` capability), not a transient failure; say so.
  if (err instanceof Error && err.message.includes('blocked by host')) {
    return t('error.openid-blocked');
  }
  if (err instanceof Error && err.message.includes('OpenID')) return t('error.auth');
  // fetch() network failures surface as TypeError; transport timeouts abort
  // with AbortError (DOMException, also instanceof Error with name).
  if (err instanceof Error && (err.name === 'TypeError' || err.name === 'AbortError')) {
    return t('error.network');
  }
  return t('error.generic', { reason: err instanceof Error ? err.message : String(err) });
};
