/* ============================================================================
 * errors.jsx — Centralized error capture + bottom error bar
 * ----------------------------------------------------------------------------
 * Why a custom layer instead of just throwing?
 *   1. Lets us assign STABLE custom error codes (WYF-XXX) so the docs in
 *      TROUBLESHOOTING.md map 1:1 to what users see.
 *   2. Surfaces *where* an error happened (view name, action) so non-technical
 *      operators can describe it precisely when reporting.
 *   3. Renders a single, dismissible bottom bar instead of toast spam.
 *
 * Usage:
 *   reportError({ code: 'WYF-DB-101', where: 'Contacts → Create',
 *                 message: 'Email already exists', detail: err.message });
 *
 *   // Or via wrap helper:
 *   await wrap('WYF-DB-200', 'Orders → Cancel', () => api.rpc('cancel_order', ...));
 *
 * The error bar lives at the bottom of the page and STAYS HIDDEN when there
 * are no errors. See ErrorBar component below.
 * ========================================================================= */

const ERROR_CODES = {
  // Auth / login
  "WYF-AUTH-001": "Invalid email or password",
  "WYF-AUTH-002": "CAPTCHA verification failed",
  "WYF-AUTH-003": "Too many failed attempts — please wait",
  "WYF-AUTH-004": "Session expired — please sign in again",
  "WYF-AUTH-005": "Password reset email failed to send",
  "WYF-AUTH-006": "Password is too weak (min 8 chars, 1 number)",
  "WYF-AUTH-007": "Account is disabled — contact an administrator",

  // Configuration
  "WYF-CFG-001": "Supabase URL or anon key missing in config.js",
  "WYF-CFG-002": "Supabase JS client failed to load (check internet/CDN)",

  // Connectivity
  "WYF-NET-001": "Network unreachable — check your internet connection",
  "WYF-NET-002": "Supabase server returned an unexpected error",
  "WYF-NET-003": "Request timed out",

  // Database queries
  "WYF-DB-100": "Permission denied (Row-Level Security blocked the request)",
  "WYF-DB-101": "Unique constraint violation (duplicate value)",
  "WYF-DB-102": "Foreign key violation (related record missing)",
  "WYF-DB-103": "Required field missing or invalid",
  "WYF-DB-104": "Record not found",
  "WYF-DB-105": "RPC function returned an error",
  "WYF-DB-106": "Read query failed",
  "WYF-DB-107": "Write query failed",

  // Validation
  "WYF-VAL-001": "Phone number is not a valid format",
  "WYF-VAL-002": "Email format is invalid",
  "WYF-VAL-003": "Required fields are empty",
  "WYF-VAL-004": "Value out of allowed range",

  // Business rules
  "WYF-BIZ-001": "Customer over credit limit",
  "WYF-BIZ-002": "Cannot delete: record is referenced elsewhere",
  "WYF-BIZ-003": "Order is already cancelled",
};

/* Subscriber model so the ErrorBar component re-renders when new errors land */
const _errorListeners = new Set();
let _currentError = null;

function reportError(err) {
  // err = { code, where, message, detail }
  const enriched = {
    code: err.code || "WYF-DB-107",
    where: err.where || "Unknown",
    message: err.message || ERROR_CODES[err.code] || "An error occurred",
    detail: err.detail || "",
    at: new Date().toISOString(),
  };
  _currentError = enriched;
  // Console log for devs
  console.error(`[${enriched.code}] ${enriched.where}: ${enriched.message}`, enriched.detail);
  _errorListeners.forEach(fn => fn(enriched));
  return enriched;
}

function clearError() {
  _currentError = null;
  _errorListeners.forEach(fn => fn(null));
}

function subscribeError(fn) {
  _errorListeners.add(fn);
  fn(_currentError);
  return () => _errorListeners.delete(fn);
}

/* Wrap an async operation. Catches and reports with the given code+where. */
async function wrap(code, where, op) {
  try {
    return await op();
  } catch (e) {
    reportError({
      code,
      where,
      message: ERROR_CODES[code] || "Operation failed",
      detail: e && (e.message || e.error_description || JSON.stringify(e)),
    });
    throw e;
  }
}

/* React component: bottom-of-screen error tray */
function ErrorBar() {
  const [err, setErr] = React.useState(null);
  React.useEffect(() => subscribeError(setErr), []);
  if (!err) return null;
  return (
    <div className="err-bar" role="alert">
      <div className="ico">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
          <circle cx="12" cy="12" r="10" />
          <line x1="12" y1="8" x2="12" y2="12" />
          <line x1="12" y1="16" x2="12" y2="16" />
        </svg>
      </div>
      <div className="body">
        <strong>{err.message}</strong>
        <code>{err.code}</code>
        <div className="where">at {err.where} · {new Date(err.at).toLocaleTimeString()}</div>
        {err.detail ? <div className="where mono" style={{marginTop:6}}>{err.detail}</div> : null}
      </div>
      <div className="actions">
        <button className="btn btn-ghost" onClick={() => {
          navigator.clipboard?.writeText(JSON.stringify(err, null, 2));
        }}>Copy</button>
        <button className="btn btn-ghost" onClick={clearError}>Dismiss</button>
      </div>
    </div>
  );
}

Object.assign(window, { ERROR_CODES, reportError, clearError, subscribeError, wrap, ErrorBar });
