const https = require("https");
const { UA } = require("../data/constants");

const BGPROUTES_API_KEY = process.env.BGPROUTES_API_KEY || "";
const BGPROUTES_API_URL = process.env.BGPROUTES_API_URL || "https://api.bgproutes.io/v1";

function fetchJSON(url, options) {
  const timeoutMs = (options && options.timeout) || 8000;
  return new Promise((resolve) => {
    const reqOptions = {
      headers: { "User-Agent": UA, ...(options && options.headers ? options.headers : {}) },
      timeout: timeoutMs,
    };
    const timer = setTimeout(() => resolve(null), timeoutMs + 500);
    https
      .get(url, reqOptions, (res) => {
        let data = "";
        res.on("data", (chunk) => (data += chunk));
        res.on("end", () => {
          clearTimeout(timer);
          if (res.statusCode === 429) {
            console.warn("[PDB] Rate limited (429):", url.substring(0, 80));
            return resolve(null);
          }
          try {
            resolve(JSON.parse(data));
          } catch (_e) {
            resolve(null);
          }
        });
      })
      .on("timeout", () => { clearTimeout(timer); resolve(null); })
      .on("error", () => { clearTimeout(timer); resolve(null); });
  });
}

// Generic JSON fetch with one retry — for sources that occasionally fail under load (RIPE Stat, Atlas)
async function fetchJSONWithRetry(url, options) {
  const result = await fetchJSON(url, options);
  if (result !== null) return result;
  await new Promise(r => setTimeout(r, 1000));
  return fetchJSON(url, options);
}

function fetchHTML(url, options) {
  return new Promise((resolve) => {
    const reqOptions = {
      headers: {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        ...(options && options.headers ? options.headers : {}),
      },
    };
    const lib = url.startsWith("https") ? require("https") : require("http");
    lib
      .get(url, reqOptions, (res) => {
        if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
          return fetchHTML(res.headers.location, options).then(resolve);
        }
        let data = "";
        res.on("data", (chunk) => (data += chunk));
        res.on("end", () => resolve(data));
      })
      .on("error", () => resolve(null));
  });
}

function postJSON(url, body, options) {
  return new Promise((resolve) => {
    const data = JSON.stringify(body);
    const parsed = new URL(url);
    const timeout = (options && options.timeout) || 10000;
    const reqOptions = {
      hostname: parsed.hostname,
      port: parsed.port || 443,
      path: parsed.pathname + parsed.search,
      method: "POST",
      headers: {
        "User-Agent": UA,
        "Content-Type": "application/json",
        "Content-Length": Buffer.byteLength(data),
        ...(options && options.headers ? options.headers : {}),
      },
    };
    let done = false;
    const timer = setTimeout(() => { if (!done) { done = true; req.destroy(); resolve(null); } }, timeout);
    const req = https.request(reqOptions, (res) => {
      let chunks = "";
      res.on("data", (chunk) => (chunks += chunk));
      res.on("end", () => {
        if (done) return;
        done = true;
        clearTimeout(timer);
        try {
          resolve(JSON.parse(chunks));
        } catch (_e) {
          resolve(null);
        }
      });
    });
    req.on("error", () => { if (!done) { done = true; clearTimeout(timer); resolve(null); } });
    req.write(data);
    req.end();
  });
}

// bgproutes.io visibility fallback helper
// Queries the RIB endpoint to estimate prefix visibility across vantage points
function fetchBgproutesVisibility(prefix) {
  if (!BGPROUTES_API_KEY) return Promise.resolve(null);
  const url = BGPROUTES_API_URL + "/rib?prefix=" + encodeURIComponent(prefix) + "&prefix_match=exact";
  return fetchJSON(url, {
    timeout: 15000,
    headers: {
      "Authorization": "Bearer " + BGPROUTES_API_KEY,
      "User-Agent": UA,
    },
  }).then(function(data) {
    if (!data || !data.data) return null;
    // data.data should be an array of RIB entries from different vantage points
    var entries = Array.isArray(data.data) ? data.data : (data.data.entries || data.data.routes || []);
    var vpSet = new Set();
    entries.forEach(function(e) {
      if (e.vantage_point || e.vp || e.collector || e.peer_asn) {
        vpSet.add(e.vantage_point || e.vp || e.collector || e.peer_asn);
      }
    });
    return { vps_seeing: vpSet.size, total_entries: entries.length, source: "bgproutes.io" };
  }).catch(function() { return null; });
}

module.exports = { fetchJSON, fetchJSONWithRetry, fetchHTML, postJSON, fetchBgproutesVisibility };
