// =====================================================================
// lib/error-sanitizer.js
// ---------------------------------------------------------------------
// Sanitasi error agar tidak bocorkan:
//   - Nama model asli (kwaivgi/\\rkling-v3-motion-control, dll)
//   - Provider backend (Weavy, Replicate, fal.ai, dll)
//   - Endpoint URL / API path backend
//   - Stack trace, internal error code, raw HTTP status
//   - Informasi akun pool internal
//
// Pendekatan:
//   1. rawError (string atau object) → categorize ke category
//   2. category → friendly Indonesian message (publik aman)
//   3. Simpan raw di job.internalError untuk admin logs (JANGAN di API publik)
//
// Skema:
//   - toPublicError(raw)         → safe string untuk response API publik
//   - toInternalRecord(raw)      → { publicMessage, category, raw } untuk job.internalError
// =====================================================================

// Daftar substring yang HARUS dihapus/disensor dari pesan publik
const REDACT_PATTERNS = [
  // URL backend
  /https?:\/\/(api\.)?weavy\.ai[^\s]*/gi,
  /https?:\/\/app\.weavy\.ai[^\s]*/gi,
  /https?:\/\/replicate\.com[^\s]*/gi,
  /https?:\/\/replicate\.delivery[^\s]*/gi,
  /https?:\/\/fal\.ai[^\s]*/gi,
  /https?:\/\/api\.fal\.ai[^\s]*/gi,
  /https?:\/\/[\w.-]*figma\.com[^\s]*/gi,
  /https?:\/\/[\w.-]*firebase[^\s]*/gi,
  // Nama model
  /kwaivgi[\/\\]+rkling-?v?\d?-?[\w-]*/gi,
  /kling-?v?\d?-?[\w-]*motion-?control/gi,
  /replicate\/?[\w-]*/gi,
  // Provider name
  /\bweavy\b/gi,
  /\breplicate\b/gi,
  /\bfal\.ai\b/gi,
  /\bfigma\b/gi,
  /\bfirebase\b/gi,
  // Internal error code
  /\binternalErrorCode[^\s)]*/gi,
  /\(code\s*\d+\)/gi,
  // Stack trace
  /\s+at\s+[\w.<>]+\s*\([\w./-]+:\d+:\d+\)/g,
  /Error:\s+[A-Z][\w]*Error/g,
  // Path internal
  /\/api\/v1\/[^\s)]*/gi,
  /\/api\/v\d+\/workspaces[^\s)]*/gi,
  // HTTP method/path leakage
  /\b(GET|POST|PUT|DELETE|PATCH)\s+\/[\w/-]+/g,
];

// Daftar kategori error yang dikenal
const CATEGORY_KEYWORDS = [
  // === Quota / Credit ===
  {
    category: "QUOTA_EXCEEDED",
    patterns: [/insufficient\s*credit/i, /quota/i, /credit\s*amount/i, /balance/i, /limit\s*reached/i, /kredit/i, /saldo/i],
    publicMessage: "Kuota generate telah habis untuk sementara. Silakan hubungi Admin untuk menambah kuota atau coba lagi nanti."
  },
  {
    category: "MODEL_LOCKED",
    patterns: [/model\s*not\s*available/i, /plan\s*lock/i, /upgrade/i, /1076/i, /pro\s*plan/i],
    publicMessage: "Fitur ini tidak tersedia pada paket akun Anda saat ini. Silakan hubungi Admin untuk informasi paket."
  },
  {
    category: "RECIPE_NOT_FOUND",
    patterns: [/recipe.*not\s*found/i, /recipe.*invalid/i, /unknown\s*recipe/i],
    publicMessage: "Konfigurasi generate tidak ditemukan. Silakan coba lagi atau hubungi Admin."
  },
  // === Auth ===
  {
    category: "AUTH_FAILED",
    patterns: [/unauthorized/i, /401/, /invalid\s*token/i, /invalid\s*grant/i, /expired\s*token/i, /auth.*fail/i, /sign.?in/i, /oauth/i, /credential/i],
    publicMessage: "Sesi generate Anda telah berakhir. Silakan coba lagi dalam beberapa saat."
  },
  {
    category: "FORBIDDEN",
    patterns: [/forbidden/i, /403/, /access\s*denied/i, /not\s*allowed/i, /permission/i],
    publicMessage: "Akses generate saat ini tidak diizinkan. Silakan coba lagi nanti atau hubungi Admin."
  },
  // === Input ===
  {
    category: "BAD_INPUT",
    patterns: [/bad\s*request/i, /400/, /invalid\s*input/i, /missing\s*param/i, /validation/i, /required/i],
    publicMessage: "Input yang Anda kirimkan tidak valid. Silakan periksa kembali dan coba lagi."
  },
  // === Server / upstream ===
  {
    category: "UPSTREAM_ERROR",
    patterns: [/500/, /502/, /503/, /504/, /internal\s*server\s*error/i, /service\s*unavailable/i, /bad\s*gateway/i, /timeout/i, /upstream/i, /provider/i],
    publicMessage: "Server generate sedang sibuk. Silakan coba lagi dalam 1-2 menit."
  },
  {
    category: "RATE_LIMITED",
    patterns: [/429/, /too\s*many\s*requests/i, /rate\s*limit/i, /throttle/i],
    publicMessage: "Terlalu banyak permintaan. Silakan tunggu beberapa saat dan coba lagi."
  },
  // === Network ===
  {
    category: "NETWORK_ERROR",
    patterns: [/econnreset/i, /enotfound/i, /etimedout/i, /fetch\s*failed/i, /network/i, /socket\s*hang\s*up/i],
    publicMessage: "Koneksi ke server generate tidak stabil. Silakan coba lagi."
  },
  // === Pool / Account ===
  {
    category: "POOL_EMPTY",
    patterns: [/no\s*account/i, /pool\s*empty/i, /tidak\s*tersedia/i, /akun\s*kosong/i],
    publicMessage: "Server sedang tidak tersedia. Silakan hubungi Admin untuk bantuan."
  },
  {
    category: "ACCOUNT_BLOCKED",
    patterns: [/account\s*lock/i, /account\s*ban/i, /diblokir/i, /suspended/i],
    publicMessage: "Server generate sedang dalam pemeliharaan. Silakan coba lagi nanti."
  },
];

// Default fallback
const DEFAULT_PUBLIC = "Generate gagal. Silakan coba lagi dalam beberapa saat atau hubungi Admin.";

/**
 * Klasifikasikan error mentah ke salah satu kategori.
 * @param {string|object|null|undefined} rawError
 * @returns {{ category: string, publicMessage: string, raw: string }}
 */
export function categorizeError(rawError) {
  const raw = stringifyError(rawError);

  for (const def of CATEGORY_KEYWORDS) {
    for (const pat of def.patterns) {
      if (pat.test(raw)) {
        return {
          category: def.category,
          publicMessage: def.publicMessage,
          raw,
        };
      }
    }
  }

  return {
    category: "UNKNOWN",
    publicMessage: DEFAULT_PUBLIC,
    raw,
  };
}

/**
 * Stringify error mentah ke string. Aman untuk di-parse.
 */
function stringifyError(err) {
  if (err == null) return "";
  if (typeof err === "string") return err;
  if (typeof err === "object") {
    if (typeof err.message === "string") return err.message;
    try {
      return JSON.stringify(err);
    } catch {
      return String(err);
    }
  }
  return String(err);
}

/**
 * Sanitasi error mentah → pesan publik AMAN.
 * Memastikan tidak ada:
 *   - URL backend
 *   - Nama model
 *   - Provider
 *   - Stack trace
 *   - Internal error code
 *
 * @param {string|object|null|undefined} rawError
 * @returns {string} pesan publik berbahasa Indonesia
 */
export function toPublicError(rawError) {
  const { publicMessage } = categorizeError(rawError);

  // Redact tambahan: kalau publicMessage mengandung pattern sensitif (mis. hasil default),
  // bersihkan dengan mask generik.
  let safe = publicMessage;
  for (const pat of REDACT_PATTERNS) {
    safe = safe.replace(pat, "[tersensor]");
  }
  return safe;
}

/**
 * Buat record error lengkap untuk disimpan di job.internalError
 * (HANYA boleh diakses admin, JANGAN diekspos via API publik).
 */
export function toInternalRecord(rawError) {
  const cat = categorizeError(rawError);
  return {
    category: cat.category,
    publicMessage: toPublicError(rawError),
    raw: cat.raw,
    timestamp: Date.now(),
  };
}

/**
 * Sanitasi output error dari Weavy API sebelum digunakan di pesan publik.
 * Mempertahankan signifikansi kesalahan (mis. "kuota habis" vs "input salah")
 * tapi menghilangkan detail teknis.
 */
export function sanitizeWeavyResponse(payload) {
  if (!payload) return toPublicError("Unknown error");
  if (typeof payload === "string") return toPublicError(payload);

  if (payload.json) {
    const j = payload.json;
    if (j && typeof j === "object") {
      if (j.statusCode === 403 && (j.internalErrorCode === 1076 || /model/i.test(j.message || ""))) {
        return toPublicError("Model not available on this plan");
      }
      if (j.statusCode === 402 || /insufficient|credit/i.test(j.message || "")) {
        return toPublicError("Insufficient credits");
      }
      if (j.statusCode === 429) {
        return toPublicError("Rate limit");
      }
      if (j.statusCode >= 500) {
        return toPublicError(`HTTP ${j.statusCode}`);
      }
      if (j.message) {
        return toPublicError(j.message);
      }
    }
    if (typeof j === "string") return toPublicError(j);
  }

  if (payload.text) return toPublicError(payload.text);
  if (payload.status) return toPublicError(`HTTP ${payload.status}`);
  return DEFAULT_PUBLIC;
}

/**
 * Sanitasi exception (catch block) sebelum dikembalikan ke publik.
 * Biasanya err.message mengandung info teknis.
 */
export function sanitizeException(err) {
  if (!err) return DEFAULT_PUBLIC;
  const msg = err && err.message ? err.message : String(err);
  return toPublicError(msg);
}
