const { crossCheckRpki } = require("../../services/rpki");

// Multi-source cross-checks (RPKI vs RIPE Validator, prefix/neighbour counts
// vs bgp.he.net) plus the overall data-quality summary derived from them.
async function computeCrossChecks(asn, allPrefixes, rpkiStatuses, prefixes, neighbours, bgpHeData) {
  let rpkiCrossCheck = { cloudflare_valid: 0, ripe_valid: 0, agreement_pct: null, disagreements: [], sample_size: 0, comparable_size: 0 };
  let prefixCrossCheck = { ripe_stat: prefixes.length, bgp_he_net: null, agreement: null, note: "" };
  let neighbourCrossCheck = { ripe_stat_total: neighbours.length, bgp_he_net_total: null };

  try {
    // RPKI cross-check: sample up to 5 prefixes against RIPE Validator (with 8s total timeout)
    const rpkiCrossPromise = crossCheckRpki(asn, allPrefixes, rpkiStatuses);
    const rpkiCrossResult = await Promise.race([
      rpkiCrossPromise,
      new Promise((resolve) => setTimeout(() => resolve(null), 8000)),
    ]);
    if (rpkiCrossResult) rpkiCrossCheck = rpkiCrossResult;
  } catch (_e) { /* cross-check failed, keep defaults */ }

  // bgp.he.net fetching is deliberately disabled (see the hardcoded
  // Promise.resolve(null) in index.js's timedFetch("bgp.he.net", ...)) --
  // bgpHeData is therefore always null and this whole branch, along with the
  // prefix/neighbour cross-checks below, never actually runs. Left in place
  // (not deleted) in case bgp.he.net fetching is re-enabled later, but
  // overall_confidence below must not claim a 2-source agreement that never
  // happened -- see the sources:1/2 handling further down.
  // Prefix count cross-check: compare RIPE Stat vs bgp.he.net
  if (bgpHeData) {
    const heV4 = bgpHeData.prefixes_v4 || 0;
    const heV6 = bgpHeData.prefixes_v6 || 0;
    const heTotal = heV4 + heV6;
    if (heTotal > 0) {
      prefixCrossCheck.bgp_he_net = heTotal;
      const ripeStat = prefixes.length;
      if (ripeStat > 0 && heTotal > 0) {
        const ratio = Math.min(ripeStat, heTotal) / Math.max(ripeStat, heTotal);
        prefixCrossCheck.agreement = ratio >= 0.9;
        const diff = Math.abs(ripeStat - heTotal);
        prefixCrossCheck.note = diff === 0
          ? "Exact match"
          : "Difference of " + diff + " prefixes (" + Math.round((1 - ratio) * 100) + "% divergence)";
      }
    } else {
      prefixCrossCheck.note = "bgp.he.net prefix count unavailable";
    }

    // Neighbour cross-check: compare RIPE Stat vs bgp.he.net peer_count
    if (bgpHeData.peer_count != null) {
      neighbourCrossCheck.bgp_he_net_total = bgpHeData.peer_count;
    }
  } else {
    prefixCrossCheck.note = "bgp.he.net data unavailable";
  }

  // Compute overall data quality. Only push a score for a cross-check that
  // actually ran (agreement_pct !== null) -- an unrun/inconclusive check must
  // not silently count as a perfect 100 in the average, and confidence must
  // reflect "nothing was actually cross-checked" rather than defaulting high.
  const crossCheckScores = [];
  if (rpkiCrossCheck.agreement_pct !== null) crossCheckScores.push(rpkiCrossCheck.agreement_pct);
  // Prefix agreement: convert to percentage
  if (prefixCrossCheck.bgp_he_net != null && prefixes.length > 0) {
    const pfxRatio = Math.min(prefixes.length, prefixCrossCheck.bgp_he_net) / Math.max(prefixes.length, prefixCrossCheck.bgp_he_net);
    crossCheckScores.push(Math.round(pfxRatio * 100));
  }
  // Neighbour agreement
  if (neighbourCrossCheck.bgp_he_net_total != null && neighbours.length > 0) {
    const nbrRatio = Math.min(neighbours.length, neighbourCrossCheck.bgp_he_net_total) / Math.max(neighbours.length, neighbourCrossCheck.bgp_he_net_total);
    crossCheckScores.push(Math.round(nbrRatio * 100));
  }
  const avgAgreement = crossCheckScores.length > 0
    ? Math.round(crossCheckScores.reduce((a, b) => a + b, 0) / crossCheckScores.length)
    : null;
  const overallConfidence = avgAgreement === null ? "unknown" : avgAgreement > 90 ? "high" : avgAgreement >= 70 ? "medium" : "low";

  const dataQuality = {
    sources_queried: ["PeeringDB", "RIPE Stat", "bgp.he.net", "Cloudflare RPKI", "RIPE RPKI Validator"],
    cross_checks: {
      rpki: { sources: rpkiCrossCheck.agreement_pct !== null ? 2 : 1, agreement_pct: rpkiCrossCheck.agreement_pct, sample_size: rpkiCrossCheck.sample_size, comparable_size: rpkiCrossCheck.comparable_size, disagreements: rpkiCrossCheck.disagreements },
      prefixes: { sources: prefixCrossCheck.bgp_he_net != null ? 2 : 1, agreement_pct: prefixCrossCheck.bgp_he_net != null ? Math.round((Math.min(prefixes.length, prefixCrossCheck.bgp_he_net) / Math.max(prefixes.length, prefixCrossCheck.bgp_he_net || 1)) * 100) : null, ripe_stat: prefixCrossCheck.ripe_stat, bgp_he_net: prefixCrossCheck.bgp_he_net, note: prefixCrossCheck.note },
      neighbours: { sources: neighbourCrossCheck.bgp_he_net_total != null ? 2 : 1, agreement_pct: neighbourCrossCheck.bgp_he_net_total != null && neighbours.length > 0 ? Math.round((Math.min(neighbours.length, neighbourCrossCheck.bgp_he_net_total) / Math.max(neighbours.length, neighbourCrossCheck.bgp_he_net_total)) * 100) : null, ripe_stat_total: neighbourCrossCheck.ripe_stat_total, bgp_he_net_total: neighbourCrossCheck.bgp_he_net_total },
    },
    overall_confidence: overallConfidence,
    overall_agreement_pct: avgAgreement,
  };

  return { rpkiCrossCheck, prefixCrossCheck, neighbourCrossCheck, dataQuality };
}

module.exports = { computeCrossChecks };
