import { describe, test, expect, mock, spyOn, beforeEach, afterEach } from "bun:test";
import { GitHubClientImpl } from "@/github/client.js";
import { collectAnalytics } from "@/analytics/metrics.js";
import { mockLogger } from "../_helpers/mock-logger.js";
import type { RepoConfig } from "@/types/index.js";

// Use spyOn instead of mock.module so the mock is scoped to this file and
// does not leak into test/github/client.test.ts or test/server/routes/debug.test.ts.

beforeEach(() => {
  spyOn(GitHubClientImpl.prototype, "listMergedPrs").mockResolvedValue([] as never);
});

afterEach(() => {
  mock.restore();
});

// ── helpers ───────────────────────────────────────────────────────────────────

function makeRepo(name: string): RepoConfig {
  return {
    name,
    url: `https://github.com/org/${name}`,
    description: "",
    keywords: [],
    defaultBranch: "main",
  } as RepoConfig;
}

// ── tests ─────────────────────────────────────────────────────────────────────

describe("collectAnalytics", () => {
  test("throws when neither startDate nor maxWeeks provided", async () => {
    await expect(
      collectAnalytics({
        logger: mockLogger(),
        repos: [],
        getGitHubToken: async () => "tok",
        botLogin: "hu-agent[bot]",
        now: new Date("2026-04-21T00:00:00Z"),
      }),
    ).rejects.toThrow("startDate");
  });

  test("throws when maxWeeks is 0", async () => {
    await expect(
      collectAnalytics({
        logger: mockLogger(),
        repos: [],
        getGitHubToken: async () => "tok",
        botLogin: "hu-agent[bot]",
        maxWeeks: 0,
        now: new Date("2026-04-21T00:00:00Z"),
      }),
    ).rejects.toThrow("collectAnalytics");
  });

  test("computes correct number of buckets for maxWeeks=2", async () => {
    const now = new Date("2026-04-21T00:00:00Z");
    const result = await collectAnalytics({
      logger: mockLogger(),
      repos: [makeRepo("my-api")],
      getGitHubToken: async () => "tok",
      botLogin: "hu-agent[bot]",
      maxWeeks: 2,
      timezone: "UTC",
      now,
    });
    expect(result.weekly.length).toBeGreaterThanOrEqual(2);
  });

  test("returns AnalyticsReport with correct meta when using startDate", async () => {
    const now = new Date("2026-04-21T12:00:00Z");
    const result = await collectAnalytics({
      logger: mockLogger(),
      repos: [makeRepo("my-api")],
      getGitHubToken: async () => "tok",
      botLogin: "hu-agent[bot]",
      startDate: new Date("2026-04-07T00:00:00Z"),
      timezone: "UTC",
      now,
    });
    expect(result.meta.generatedAt).toBe(now.toISOString());
    expect(result.meta.botLogin).toBe("hu-agent[bot]");
    expect(result.meta.reposScanned).toBe(1);
  });

  test("meta.startDate matches the first bucket's weekStart", async () => {
    const now = new Date("2026-04-21T00:00:00Z");
    const result = await collectAnalytics({
      logger: mockLogger(),
      repos: [],
      getGitHubToken: async () => "tok",
      botLogin: "hu-agent[bot]",
      startDate: new Date("2026-04-13T00:00:00Z"),
      timezone: "UTC",
      now,
    });
    expect(result.meta.startDate).toBe("2026-04-13");
  });

  test("default timezone is America/New_York when not provided", async () => {
    const result = await collectAnalytics({
      logger: mockLogger(),
      repos: [],
      getGitHubToken: async () => "tok",
      botLogin: "hu-agent[bot]",
      maxWeeks: 1,
      now: new Date("2026-04-21T00:00:00Z"),
    });
    expect(result.meta.timezone).toBe("America/New_York");
  });

  test("explicit timezone is forwarded to meta", async () => {
    const result = await collectAnalytics({
      logger: mockLogger(),
      repos: [],
      getGitHubToken: async () => "tok",
      botLogin: "hu-agent[bot]",
      maxWeeks: 1,
      timezone: "America/Argentina/Buenos_Aires",
      now: new Date("2026-04-21T00:00:00Z"),
    });
    expect(result.meta.timezone).toBe("America/Argentina/Buenos_Aires");
  });
});
