import { describe, it, expect } from 'vitest';
import { buildSummary } from './summary';
import type { FetchedStats } from './github';
import type {
  IssueSummary,
  PullSummary,
  ReleaseSummary,
  ContributorSummary,
} from './types';

const fixed = (iso: string) => iso;

const issue = (over: Partial<IssueSummary> = {}): IssueSummary => ({
  number: 1,
  title: 't',
  state: 'open',
  user: 'octocat',
  createdAt: '2026-01-01T00:00:00Z',
  updatedAt: '2026-01-01T00:00:00Z',
  comments: 0,
  labels: [],
  pullRequest: false,
  ...over,
});

const pull = (over: Partial<PullSummary> = {}): PullSummary => ({
  number: 1,
  title: 't',
  state: 'open',
  user: 'octocat',
  createdAt: '2026-01-01T00:00:00Z',
  updatedAt: '2026-01-01T00:00:00Z',
  mergedAt: null,
  draft: false,
  reviewComments: 0,
  additions: 1,
  deletions: 0,
  changedFiles: 1,
  ...over,
});

const release = (over: Partial<ReleaseSummary> = {}): ReleaseSummary => ({
  tagName: 'v1.0.0',
  name: null,
  publishedAt: '2026-05-01T00:00:00Z',
  draft: false,
  prerelease: false,
  author: 'octocat',
  ...over,
});

const contributor = (login: string, contributions: number): ContributorSummary => ({
  login,
  contributions,
});

const NOW = new Date('2026-06-01T00:00:00Z').getTime();
const realDateNow = Date.now;
Date.now = () => NOW;

afterAllRestore();

function afterAllRestore() {
  // vitest's afterAll lives in describe scope, but we can restore here.
  setTimeout(() => {
    Date.now = realDateNow;
  }, 0);
}

const baseRepo: FetchedStats['repo'] = {
  repo: { owner: 'x', name: 'y' },
  description: 'd',
  primaryLanguage: 'TypeScript',
  license: 'MIT',
  stars: 100,
  forks: 5,
  watchers: 10,
  openIssues: 2,
  defaultBranch: 'main',
  topics: [],
  createdAt: '2025-01-01T00:00:00Z',
  updatedAt: '2026-05-01T00:00:00Z',
  pushedAt: '2026-05-15T00:00:00Z',
  sizeKb: 1024,
  hasWiki: false,
  hasPages: false,
  archived: false,
  homepage: null,
};

describe('buildSummary', () => {
  it('counts open/closed issues correctly', () => {
    const fetched: FetchedStats = {
      repo: baseRepo,
      issues: [issue({ state: 'open' }), issue({ state: 'open' }), issue({ state: 'closed' })],
      pulls: [],
      releases: [],
      contributors: [],
    };
    const s = buildSummary(fetched);
    expect(s.issues.open).toBe(2);
    expect(s.issues.closed).toBe(1);
    expect(s.issues.total).toBe(3);
  });

  it('computes 7-day and 30-day windows', () => {
    const oneDayAgo = new Date(NOW - 1 * 24 * 60 * 60 * 1000).toISOString();
    const tenDaysAgo = new Date(NOW - 10 * 24 * 60 * 60 * 1000).toISOString();
    const sixtyDaysAgo = new Date(NOW - 60 * 24 * 60 * 60 * 1000).toISOString();
    const fetched: FetchedStats = {
      repo: baseRepo,
      issues: [
        issue({ createdAt: oneDayAgo }),
        issue({ createdAt: tenDaysAgo }),
        issue({ createdAt: sixtyDaysAgo }),
      ],
      pulls: [],
      releases: [],
      contributors: [],
    };
    const s = buildSummary(fetched);
    expect(s.issues.last7d.total).toBe(1);
    expect(s.issues.last30d.total).toBe(2);
  });

  it('counts merged vs closed PRs', () => {
    const fetched: FetchedStats = {
      repo: baseRepo,
      issues: [],
      pulls: [pull({ state: 'merged' }), pull({ state: 'closed' }), pull({ state: 'open' })],
      releases: [],
      contributors: [],
    };
    const s = buildSummary(fetched);
    expect(s.pulls.merged).toBe(1);
    expect(s.pulls.closed).toBe(1);
    expect(s.pulls.open).toBe(1);
  });

  it('aggregates top issue labels', () => {
    const fetched: FetchedStats = {
      repo: baseRepo,
      issues: [
        issue({ labels: ['bug', 'help wanted'] }),
        issue({ labels: ['bug'] }),
        issue({ labels: ['bug', 'good first issue'] }),
      ],
      pulls: [],
      releases: [],
      contributors: [],
    };
    const s = buildSummary(fetched);
    expect(s.issues.topLabels[0]).toEqual({ label: 'bug', count: 3 });
  });

  it('ranks top contributors', () => {
    const fetched: FetchedStats = {
      repo: baseRepo,
      issues: [],
      pulls: [],
      releases: [],
      contributors: [contributor('a', 5), contributor('b', 50), contributor('c', 20)],
    };
    const s = buildSummary(fetched);
    expect(s.contributors.top[0]?.login).toBe('b');
    expect(s.contributors.top[1]?.login).toBe('c');
  });

  it('computes release cadence', () => {
    // Three releases with 30-day spacing: 2026-05-01, 2026-04-01, 2026-03-01.
    // Total span = 61 days across 2 intervals -> 30.5 days per interval.
    const fetched: FetchedStats = {
      repo: baseRepo,
      issues: [],
      pulls: [],
      releases: [
        release({ tagName: 'v2.0.0', publishedAt: '2026-05-01T00:00:00Z' }),
        release({ tagName: 'v1.5.0', publishedAt: '2026-04-01T00:00:00Z' }),
        release({ tagName: 'v1.0.0', publishedAt: '2026-03-01T00:00:00Z' }),
      ],
      contributors: [],
    };
    const s = buildSummary(fetched);
    expect(s.releases.latest?.tagName).toBe('v2.0.0');
    expect(s.releases.cadenceDays).toBe(30.5);
  });
});
