/**
 * eBay Marketplace Velocity Scraper
 *
 * Tracks SOLD listing counts and price distributions to measure
 * actual market demand velocity — a 1-3 month leading indicator.
 *
 * Scrapes eBay's public completed/sold listings page (no API needed).
 * Looks at: sold count last 30 days, active listing count, price spread.
 *
 * High sold velocity + rising prices → buy signal
 * Falling velocity + dropping prices → commodity transition in progress
 */

import * as cheerio from "cheerio";
import { pool } from "../utils/db";
import { logger } from "../utils/logger";

const 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",
  "Accept-Language": "en-US,en;q=0.9",
  "Accept": "text/html,application/xhtml+xml,application/xhtml,application/xml;q=0.9,*/*;q=0.8",
};

const SEARCH_TERMS: Array<{ keyword: string; formFactor: string; speedLabel: string }> = [
  { keyword: "QSFP28 100G transceiver",     formFactor: "QSFP28",   speedLabel: "100G"  },
  { keyword: "QSFP+ 40G transceiver",       formFactor: "QSFP+",    speedLabel: "40G"   },
  { keyword: "QSFP28 400G transceiver",     formFactor: "QSFP28",   speedLabel: "400G"  },
  { keyword: "QSFP-DD 400G transceiver",    formFactor: "QSFP-DD",  speedLabel: "400G"  },
  { keyword: "QSFP-DD 800G transceiver",    formFactor: "QSFP-DD",  speedLabel: "800G"  },
  { keyword: "SFP+ 10G transceiver",        formFactor: "SFP+",     speedLabel: "10G"   },
  { keyword: "OSFP 400G transceiver",       formFactor: "OSFP",     speedLabel: "400G"  },
  { keyword: "CFP2 100G coherent DCO",      formFactor: "CFP2",     speedLabel: "100G"  },
  { keyword: "400ZR coherent transceiver",  formFactor: "QSFP-DD",  speedLabel: "400G-ZR"},
];

function parsePrice(text: string): number | null {
  const m = text.replace(/,/g, "").match(/[\$£€]?\s*([\d.]+)/);
  return m ? parseFloat(m[1]) : null;
}

async function scrapeEbaySoldListings(
  keyword: string
): Promise<{ soldCount: number; activeCount: number; avgSoldPrice: number | null; minPrice: number | null; maxPrice: number | null }> {

  const enc = encodeURIComponent(keyword);
  // Completed (sold) listings
  const soldUrl = `https://www.ebay.com/sch/i.html?_nkw=${enc}&LH_Complete=1&LH_Sold=1&_sop=12&_ipg=200`;

  const res = await fetch(soldUrl, { headers: HEADERS, signal: AbortSignal.timeout(20000) });
  if (!res.ok) throw new Error(`eBay returned ${res.status}`);
  const html = await res.text();
  const $ = cheerio.load(html);

  const prices: number[] = [];
  let soldCount = 0;

  // Count sold items and extract prices
  $(".s-item").each((_, el) => {
    const priceText = $(el).find(".s-item__price").first().text().trim();
    const price = parsePrice(priceText);
    const isSold = $(el).find(".POSITIVE").length > 0 || $(el).find(".s-item__caption--signal").text().includes("Sold");
    if (price && price > 0 && price < 50000) {
      prices.push(price);
      if (isSold) soldCount++;
    }
  });

  // Total count from results summary
  const resultText = $(".srp-controls__count-heading").text();
  const totalMatch = resultText.replace(/,/g, "").match(/([\d,]+)/);
  if (totalMatch) soldCount = Math.max(soldCount, parseInt(totalMatch[1]));

  // Active listings (separate search)
  await new Promise(r => setTimeout(r, 1500));
  const activeUrl = `https://www.ebay.com/sch/i.html?_nkw=${enc}&_sop=12&_ipg=1&LH_BIN=1`;
  let activeCount = 0;
  try {
    const activeRes = await fetch(activeUrl, { headers: HEADERS, signal: AbortSignal.timeout(15000) });
    if (activeRes.ok) {
      const activeHtml = await activeRes.text();
      const $a = cheerio.load(activeHtml);
      const activeText = $a(".srp-controls__count-heading").text().replace(/,/g, "");
      const am = activeText.match(/([\d]+)/);
      if (am) activeCount = parseInt(am[1]);
    }
  } catch { /* ignore active count failure */ }

  const avgSoldPrice = prices.length > 0
    ? Math.round(prices.reduce((a, b) => a + b, 0) / prices.length * 100) / 100
    : null;

  return {
    soldCount,
    activeCount,
    avgSoldPrice,
    minPrice: prices.length > 0 ? Math.min(...prices) : null,
    maxPrice: prices.length > 0 ? Math.max(...prices) : null,
  };
}

export async function scrapeEbayVelocity(): Promise<void> {
  logger.info("eBay velocity scraper starting");
  let recorded = 0;

  for (const term of SEARCH_TERMS) {
    try {
      logger.info(`Checking eBay velocity: ${term.keyword}`);
      await new Promise(r => setTimeout(r, 3000)); // be gentle

      const result = await scrapeEbaySoldListings(term.keyword);

      await pool.query(`
        INSERT INTO marketplace_velocity
          (marketplace, keyword, form_factor, speed_label, sold_count_30d,
           active_listings, avg_sold_price, min_price, max_price, currency)
        VALUES ('ebay', $1, $2, $3, $4, $5, $6, $7, $8, 'USD')
      `, [
        term.keyword, term.formFactor, term.speedLabel,
        result.soldCount, result.activeCount, result.avgSoldPrice,
        result.minPrice, result.maxPrice,
      ]);
      recorded++;

      logger.info(
        `${term.speedLabel} ${term.formFactor}: ${result.soldCount} sold, ` +
        `${result.activeCount} active, avg $${result.avgSoldPrice ?? "?"}`
      );
    } catch (err) {
      logger.warn(`eBay velocity failed: ${term.keyword}`, { err });
    }
  }

  logger.info(`eBay velocity scraper done — ${recorded} records`);
}
