/**
 * Higher-level summarization.
 *
 * Given the raw output of `fetchRepoStats`, produce aggregates that
 * are useful for a maintainer dashboard:
 *   - 7-day and 30-day activity windows
 *   - top contributors
 *   - issue/PR throughput
 */

import type {
  ReleaseSummary,
  ContributorSummary,
} from './types.js';
import type { FetchedStats } from './github.js';

export type { FetchedStats };

export interface ActivityBucket {
  open: number;
  closed: number;
  total: number;
}

export interface RepoSummary {
  fetched: FetchedStats;
  issues: {
    open: number;
    closed: number;
    total: number;
    last7d: ActivityBucket;
    last30d: ActivityBucket;
    topLabels: Array<{ label: string; count: number }>;
  };
  pulls: {
    open: number;
    closed: number;
    merged: number;
    total: number;
    last7d: { opened: number; merged: number };
    last30d: { opened: number; merged: number };
    avgChangedFiles: number;
  };
  releases: {
    total: number;
    latest: ReleaseSummary | null;
    cadenceDays: number | null;
  };
  contributors: {
    total: number;
    top: ContributorSummary[];
  };
}

function inWindow(iso: string, days: number): boolean {
  const t = new Date(iso).getTime();
  if (Number.isNaN(t)) return false;
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
  return t >= cutoff;
}

function topEntries<K>(map: Map<K, number>, n: number): Array<{ key: K; count: number }> {
  return [...map.entries()]
    .map(([key, count]) => ({ key, count }))
    .sort((a, b) => b.count - a.count)
    .slice(0, n);
}

export function buildSummary(fetched: FetchedStats): RepoSummary {
  const { issues, pulls, releases, contributors } = fetched;

  const issueOpen = issues.filter((i) => i.state === 'open').length;
  const issueClosed = issues.filter((i) => i.state === 'closed').length;

  const issueWindow7 = { open: 0, closed: 0, total: 0 };
  const issueWindow30 = { open: 0, closed: 0, total: 0 };
  const labelCounts = new Map<string, number>();
  for (const i of issues) {
    if (inWindow(i.createdAt, 7)) {
      issueWindow7[i.state] += 1;
      issueWindow7.total += 1;
    }
    if (inWindow(i.createdAt, 30)) {
      issueWindow30[i.state] += 1;
      issueWindow30.total += 1;
    }
    for (const l of i.labels) {
      labelCounts.set(l, (labelCounts.get(l) ?? 0) + 1);
    }
  }

  const pullOpen = pulls.filter((p) => p.state === 'open').length;
  const pullClosed = pulls.filter((p) => p.state === 'closed').length;
  const pullMerged = pulls.filter((p) => p.state === 'merged').length;

  let opened7 = 0;
  let merged7 = 0;
  let opened30 = 0;
  let merged30 = 0;
  let filesTotal = 0;
  for (const p of pulls) {
    if (inWindow(p.createdAt, 7)) {
      opened7 += 1;
      if (p.mergedAt) merged7 += 1;
    }
    if (inWindow(p.createdAt, 30)) {
      opened30 += 1;
      if (p.mergedAt) merged30 += 1;
    }
    filesTotal += p.changedFiles;
  }
  const avgChangedFiles = pulls.length === 0 ? 0 : Math.round((filesTotal / pulls.length) * 10) / 10;

  const datedReleases = releases
    .filter((r) => r.publishedAt)
    .sort((a, b) => new Date(b.publishedAt!).getTime() - new Date(a.publishedAt!).getTime());
  const latest = datedReleases[0] ?? null;
  let cadenceDays: number | null = null;
  if (datedReleases.length >= 2) {
    const newest = new Date(datedReleases[0]!.publishedAt!).getTime();
    const oldest = new Date(datedReleases[datedReleases.length - 1]!.publishedAt!).getTime();
    const span = newest - oldest;
    cadenceDays = Math.round((span / (datedReleases.length - 1)) / (24 * 60 * 60 * 1000) * 10) / 10;
  }

  const sortedContributors = [...contributors].sort((a, b) => b.contributions - a.contributions);

  return {
    fetched,
    issues: {
      open: issueOpen,
      closed: issueClosed,
      total: issues.length,
      last7d: issueWindow7,
      last30d: issueWindow30,
      topLabels: topEntries(labelCounts, 5).map((e) => ({ label: e.key, count: e.count })),
    },
    pulls: {
      open: pullOpen,
      closed: pullClosed,
      merged: pullMerged,
      total: pulls.length,
      last7d: { opened: opened7, merged: merged7 },
      last30d: { opened: opened30, merged: merged30 },
      avgChangedFiles,
    },
    releases: {
      total: releases.length,
      latest,
      cadenceDays,
    },
    contributors: {
      total: sortedContributors.length,
      top: sortedContributors.slice(0, 5),
    },
  };
}

export function summarizeContributors(
  contributors: ContributorSummary[],
  top = 10,
): ContributorSummary[] {
  return [...contributors].sort((a, b) => b.contributions - a.contributions).slice(0, top);
}
