/**
 * GitHub Technology Adoption Signal Collector
 *
 * Tracks how fast key networking technologies are adopted in open-source
 * projects by measuring commit/repo/star activity weekly.
 *
 * Technologies tracked: 400G, 800G, ZR, CPO, silicon-photonics, CMIS, 1.6T
 *
 * Key repos monitored:
 *   - sonic-net/sonic-buildimage (SONiC NOS)
 *   - openconfig/public (vendor-neutral config)
 *   - DPDK/dpdk (data plane)
 *   - Arista/sonic-mgmt
 *   - netdisco/netdisco
 *
 * GitHub activity is a 6-18 month leading indicator for enterprise adoption.
 */

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

const GITHUB_API = "https://api.github.com";
const TOKEN = process.env.GITHUB_TOKEN || "";
const HEADERS: Record<string, string> = {
  "User-Agent":  "TIP-DataCollector/1.0",
  "Accept":      "application/vnd.github.v3+json",
  ...(TOKEN ? { "Authorization": `Bearer ${TOKEN}` } : {}),
};

const TECHNOLOGIES = [
  { key: "400G",             queries: ["400G transceiver", "QSFP28 400G", "400GbE"] },
  { key: "800G",             queries: ["800G transceiver", "QSFP-DD 800G", "800GbE"] },
  { key: "ZR",               queries: ["400ZR transceiver", "OpenZR+ optical", "coherent ZR"] },
  { key: "CPO",              queries: ["co-packaged optics", "CPO networking"] },
  { key: "silicon-photonics",queries: ["silicon photonics transceiver", "SiPh networking"] },
  { key: "CMIS",             queries: ["CMIS 5.0 transceiver", "CMIS optical module"] },
  { key: "1.6T",             queries: ["1.6T ethernet", "1.6Tbps networking"] },
  { key: "OSFP",             queries: ["OSFP transceiver", "OSFP-XD module"] },
];

const KEY_REPOS = [
  "sonic-net/sonic-buildimage",
  "openconfig/public",
  "DPDK/dpdk",
  "netdisco/netdisco",
  "FRRouting/frr",
];

async function githubGet<T>(path: string): Promise<T> {
  const res = await fetch(`${GITHUB_API}${path}`, { headers: HEADERS });
  if (res.status === 403) throw new Error("GitHub rate limited");
  if (!res.ok) throw new Error(`GitHub API error: ${res.status} ${path}`);
  return res.json() as Promise<T>;
}

async function searchRepoCount(query: string): Promise<number> {
  const enc = encodeURIComponent(query);
  const data = await githubGet<{ total_count: number }>(
    `/search/repositories?q=${enc}&per_page=1`
  );
  return data.total_count;
}

async function searchCodeCount(query: string): Promise<number> {
  const enc = encodeURIComponent(query);
  const data = await githubGet<{ total_count: number }>(
    `/search/code?q=${enc}&per_page=1`
  );
  return data.total_count;
}

async function getRepoWeeklyCommits(repo: string): Promise<number> {
  // Returns commits in last 52 weeks, we take last week
  const data = await githubGet<Array<{ total: number }>>(
    `/repos/${repo}/stats/commit_activity`
  );
  if (!Array.isArray(data) || data.length === 0) return 0;
  return data[data.length - 1]?.total ?? 0;
}

async function getRepoStars(repo: string): Promise<number> {
  const data = await githubGet<{ stargazers_count: number }>(
    `/repos/${repo}`
  );
  return data.stargazers_count;
}

function getWeekStart(): Date {
  const d = new Date();
  d.setUTCHours(0, 0, 0, 0);
  d.setUTCDate(d.getUTCDate() - d.getUTCDay()); // Sunday
  return d;
}

async function upsertSignal(technology: string, metric: string, context: string, value: number, weekStart: Date) {
  await pool.query(`
    INSERT INTO github_tech_signals (technology, metric, repo_context, value, week_start)
    VALUES ($1, $2, $3, $4, $5)
    ON CONFLICT (technology, metric, repo_context, week_start)
    DO UPDATE SET value = EXCLUDED.value
  `, [technology, metric, context, value, weekStart]);
}

export async function scrapeGithubSignals(): Promise<void> {
  logger.info("GitHub signals scraper starting");
  const weekStart = getWeekStart();
  let signalCount = 0;

  // 1. Technology repo count (how many repos mention each tech)
  for (const tech of TECHNOLOGIES) {
    let totalRepos = 0;
    for (const q of tech.queries) {
      try {
        await new Promise(r => setTimeout(r, 1200)); // stay under rate limit
        const count = await searchRepoCount(`${q} language:Python language:Go language:TypeScript language:C`);
        totalRepos += count;
      } catch (err) {
        logger.warn(`GitHub repo search failed: ${q}`, { err });
      }
    }
    await upsertSignal(tech.key, "repo_count", "all", totalRepos, weekStart);
    signalCount++;
    logger.info(`${tech.key}: ${totalRepos} repos`);
    await new Promise(r => setTimeout(r, 2000));
  }

  // 2. Key repo commit activity (weekly)
  for (const repo of KEY_REPOS) {
    try {
      await new Promise(r => setTimeout(r, 1000));
      const commits = await getRepoWeeklyCommits(repo);
      const stars   = await getRepoStars(repo);
      await upsertSignal("all-networking", "weekly_commits", repo, commits, weekStart);
      await upsertSignal("all-networking", "stars",          repo, stars,   weekStart);
      signalCount += 2;
      logger.info(`${repo}: ${commits} commits last week, ${stars} stars`);
    } catch (err) {
      logger.warn(`GitHub repo stats failed: ${repo}`, { err });
    }
  }

  // 3. SONiC-specific tech signals (most valuable for transceiver market)
  const sonicTechSearches = [
    { tech: "400G",  q: "400G repo:sonic-net/sonic-buildimage" },
    { tech: "800G",  q: "800G repo:sonic-net/sonic-buildimage" },
    { tech: "CMIS",  q: "CMIS repo:sonic-net/sonic-buildimage" },
    { tech: "OSFP",  q: "OSFP repo:sonic-net/sonic-buildimage" },
    { tech: "ZR",    q: "400ZR repo:sonic-net/sonic-buildimage" },
  ];
  for (const s of sonicTechSearches) {
    try {
      await new Promise(r => setTimeout(r, 1500));
      const count = await searchCodeCount(s.q);
      await upsertSignal(s.tech, "sonic_code_refs", "sonic-net/sonic-buildimage", count, weekStart);
      signalCount++;
      logger.info(`SONiC ${s.tech}: ${count} code references`);
    } catch (err) {
      logger.warn(`SONiC code search failed: ${s.q}`, { err });
    }
  }

  logger.info(`GitHub signals scraper done — ${signalCount} signals recorded`);
}
