const localDb = require("../../local-db-client");
const { fetchRipeStatCached } = require("../services/ripe-stat");
const { fetchPeeringDB } = require("../services/peeringdb");
const { validateRPKIWithCache } = require("../services/rpki");
const { cacheGet, cacheSet, CACHE_TTL_DEFAULT } = require("../caches/response-caches");

// Compare endpoint: /api/compare?asn1=X&asn2=Y
async function handleCompare(req, res, url) {
    const asn1 = (url.searchParams.get("asn1") || "").replace(/[^0-9]/g, "");
    const asn2 = (url.searchParams.get("asn2") || "").replace(/[^0-9]/g, "");
    if (!asn1 || !asn2) {
      res.writeHead(400);
      return res.end(JSON.stringify({ error: "Need asn1 and asn2 parameters" }));
    }

    const compareCacheKey = "compare:" + asn1 + ":" + asn2;
    const compareCached = cacheGet(compareCacheKey);
    if (compareCached) {
      res.writeHead(200, { "Content-Type": "application/json", "X-Cache": "HIT" });
      return res.end(JSON.stringify(compareCached));
    }
    const start = Date.now();
    try {
      // ALL calls in parallel — single batch
      // Phase 1: Get PDB net objects + RIPE data
      const [pdb1, pdb2, nb1Data, nb2Data, pfx1Data, pfx2Data] = await Promise.all([
        fetchPeeringDB("/net?asn=" + asn1),
        fetchPeeringDB("/net?asn=" + asn2),
        fetchRipeStatCached("https://stat.ripe.net/data/asn-neighbours/data.json?resource=AS" + asn1, { timeout: 8000 }),
        fetchRipeStatCached("https://stat.ripe.net/data/asn-neighbours/data.json?resource=AS" + asn2, { timeout: 8000 }),
        fetchRipeStatCached("https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS" + asn1, { timeout: 8000 }),
        fetchRipeStatCached("https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS" + asn2, { timeout: 8000 }),
      ]);

      const net1 = pdb1?.data?.[0] || {};
      const net2 = pdb2?.data?.[0] || {};
      const netId1 = net1.id;
      const netId2 = net2.id;

      // Phase 2: IX + Facility using net_id (Bug 1 fix: netfac requires net_id, not asn)
      const ixFacPromises = [];
      ixFacPromises.push(netId1 ? fetchPeeringDB("/netixlan?net_id=" + netId1) : Promise.resolve(null));
      ixFacPromises.push(netId2 ? fetchPeeringDB("/netixlan?net_id=" + netId2) : Promise.resolve(null));
      ixFacPromises.push(netId1 ? fetchPeeringDB("/netfac?net_id=" + netId1) : Promise.resolve(null));
      ixFacPromises.push(netId2 ? fetchPeeringDB("/netfac?net_id=" + netId2) : Promise.resolve(null));
      const [ix1Data, ix2Data, fac1Data, fac2Data] = await Promise.all(ixFacPromises);

      const ix1Set = new Set((ix1Data?.data || []).map((ix) => ix.ix_id));
      const ix2Set = new Set((ix2Data?.data || []).map((ix) => ix.ix_id));
      const ix1Names = {};
      (ix1Data?.data || []).forEach((ix) => (ix1Names[ix.ix_id] = ix.name));
      const ix2Names = {};
      (ix2Data?.data || []).forEach((ix) => (ix2Names[ix.ix_id] = ix.name));

      const commonIX = [...ix1Set].filter((id) => ix2Set.has(id)).map((id) => ({ ix_id: id, name: ix1Names[id] || ix2Names[id] || "" }));
      const only1IX = [...ix1Set].filter((id) => !ix2Set.has(id)).map((id) => ({ ix_id: id, name: ix1Names[id] || "" }));
      const only2IX = [...ix2Set].filter((id) => !ix1Set.has(id)).map((id) => ({ ix_id: id, name: ix2Names[id] || "" }));

      const fac1Set = new Set((fac1Data?.data || []).map((f) => f.fac_id));
      const fac2Set = new Set((fac2Data?.data || []).map((f) => f.fac_id));
      const fac1Names = {};
      (fac1Data?.data || []).forEach((f) => (fac1Names[f.fac_id] = f.name));
      const fac2Names = {};
      (fac2Data?.data || []).forEach((f) => (fac2Names[f.fac_id] = f.name));

      const commonFac = [...fac1Set].filter((id) => fac2Set.has(id)).map((id) => ({ fac_id: id, name: fac1Names[id] || fac2Names[id] || "" }));

      const nb1 = (nb1Data?.data?.neighbours || []).filter((n) => n.type === "left");
      const nb2 = (nb2Data?.data?.neighbours || []).filter((n) => n.type === "left");
      const up1Set = new Set(nb1.map((n) => n.asn));
      const up2Set = new Set(nb2.map((n) => n.asn));
      const nb1Map = {};
      nb1.forEach((n) => (nb1Map[n.asn] = n.as_name || ""));
      const nb2Map = {};
      nb2.forEach((n) => (nb2Map[n.asn] = n.as_name || ""));

      const commonUpstreams = [...up1Set]
        .filter((a) => up2Set.has(a))
        .map((a) => ({ asn: a, name: nb1Map[a] || nb2Map[a] || "" }));

      // ---- Threat Intelligence Enrichment for Common Upstreams ----
      const threatMap = {};
      const threatPromises = commonUpstreams.slice(0, 50).map(async (n) => {
        try {
          const asNum = String(n.asn);
          const threat = await localDb.getThreatIntel(asNum);
          if (threat) {
            threatMap[n.asn] = {
              threat_level: threat.threat_level,
              confidence_score: threat.confidence_score,
              source: threat.source,
            };
          }
        } catch (e) {
          console.error(`[Compare Threat Lookup] Error checking ASN ${n.asn}:`, e.message);
        }
      });
      await Promise.race([
        Promise.all(threatPromises),
        new Promise(r => setTimeout(r, 2000)),
      ]);

      // Attach threat status to upstream objects
      commonUpstreams.forEach((n) => {
        if (threatMap[n.asn]) {
          n.threat_level = threatMap[n.asn].threat_level;
          n.threat_confidence = threatMap[n.asn].confidence_score;
          n.threat_source = threatMap[n.asn].source;
        }
      });

      // Resolve names + RPKI sample (max 3+3 prefixes) all in parallel with 5s timeout
      const pfx1 = (pfx1Data?.data?.prefixes || []).slice(0, 3).map((p) => p.prefix);
      const pfx2 = (pfx2Data?.data?.prefixes || []).slice(0, 3).map((p) => p.prefix);
      const [, rpki1Results, rpki2Results] = await Promise.race([
        Promise.all([
          commonUpstreams.length > 0 ? Promise.all(commonUpstreams.map(n =>
            fetchRipeStatCached("https://stat.ripe.net/data/as-overview/data.json?resource=AS" + n.asn, { timeout: 3000 })
              .then(r => { if (r?.data?.holder) n.name = r.data.holder; })
              .catch(() => {})
          )) : Promise.resolve([]),
          Promise.all(pfx1.map((p) => validateRPKIWithCache(asn1, p))),
          Promise.all(pfx2.map((p) => validateRPKIWithCache(asn2, p))),
        ]),
        new Promise(r => setTimeout(() => r([[], [], []]), 5000)),
      ]);

      const rpki1Valid = rpki1Results.filter((r) => r.status === "valid").length;
      const rpki2Valid = rpki2Results.filter((r) => r.status === "valid").length;
      const rpki1Pct = rpki1Results.length > 0 ? Math.round((rpki1Valid / rpki1Results.length) * 100) : 0;
      const rpki2Pct = rpki2Results.length > 0 ? Math.round((rpki2Valid / rpki2Results.length) * 100) : 0;

      const duration = Date.now() - start;
      const compareResult = {
            meta: { duration_ms: duration, timestamp: new Date().toISOString() },
            asn1: {
              asn: parseInt(asn1),
              name: net1.name || "Unknown",
              ix_count: ix1Set.size,
              fac_count: fac1Set.size,
              upstream_count: up1Set.size,
              rpki_coverage: rpki1Pct,
            },
            asn2: {
              asn: parseInt(asn2),
              name: net2.name || "Unknown",
              ix_count: ix2Set.size,
              fac_count: fac2Set.size,
              upstream_count: up2Set.size,
              rpki_coverage: rpki2Pct,
            },
            common_ixps: commonIX,
            only_asn1_ixps: only1IX,
            only_asn2_ixps: only2IX,
            common_facilities: commonFac,
            common_upstreams: commonUpstreams,
            rpki_comparison: {
              asn1_coverage: rpki1Pct,
              asn2_coverage: rpki2Pct,
              asn1_checked: rpki1Results.length,
              asn2_checked: rpki2Results.length,
              better: rpki1Pct > rpki2Pct ? "AS" + asn1 : rpki2Pct > rpki1Pct ? "AS" + asn2 : "equal",
            },
          };
      cacheSet(compareCacheKey, compareResult, CACHE_TTL_DEFAULT);
      res.end(JSON.stringify(compareResult, null, 2));
    } catch (err) {
      res.writeHead(500);
      res.end(JSON.stringify({ error: "Compare failed", message: err.message }));
    }
    return;

}

module.exports = { handleCompare };
