import type { Adapter, FetchLike } from "../adapter";
import type { AdapterResult } from "../schema";
import type { GithubCheckRow, RequestsStore } from "../requests/requests-store";

const DEFAULT_INTERVAL_MS = 30_000;
const API_ROOT = "https://api.github.com";

interface WorkflowRun {
  head_sha: string;
  head_branch: string | null;
}
interface CheckRun {
  id: number;
  name: string;
  status: string;
  conclusion: string | null;
  head_sha: string;
  started_at: string | null;
  completed_at: string | null;
}

export interface GithubChecksAdapterOptions {
  repo: string;
  token: string;
  requests: RequestsStore;
  fetchImpl?: FetchLike;
  interval?: number;
  now?: () => number;
}

async function githubJson<T>(fetchImpl: FetchLike, url: string, token: string): Promise<T> {
  const response = await fetchImpl(url, { headers: {
    accept: "application/vnd.github+json",
    authorization: `Bearer ${token}`,
    "x-github-api-version": "2022-11-28",
    "user-agent": "overdeck-collector",
  }});
  if (!response.ok) throw new Error(`GitHub checks API returned ${response.status}`);
  return response.json() as Promise<T>;
}

export function createGithubChecksAdapter(options: GithubChecksAdapterOptions): Adapter {
  const fetchImpl = options.fetchImpl ?? fetch;
  const now = options.now ?? Date.now;
  return {
    id: "github-checks",
    interval: options.interval ?? DEFAULT_INTERVAL_MS,
    async poll(): Promise<AdapterResult> {
      const runs = await githubJson<{ workflow_runs: WorkflowRun[] }>(fetchImpl,
        `${API_ROOT}/repos/${options.repo}/actions/runs?per_page=20`, options.token);
      const refs = new Map(runs.workflow_runs.map((run) => [run.head_sha, run.head_branch]));
      const observedAt = new Date(now()).toISOString();
      for (const [sha, branch] of refs) {
        const response = await githubJson<{ check_runs: CheckRun[] }>(fetchImpl,
          `${API_ROOT}/repos/${options.repo}/commits/${sha}/check-runs?per_page=100`, options.token);
        for (const run of response.check_runs) options.requests.recordGithubCheck({
          repo: options.repo, run_id: run.id, name: run.name, status: run.status,
          conclusion: run.conclusion, sha: run.head_sha || sha, branch,
          started_at: run.started_at, completed_at: run.completed_at, observed_at: observedAt,
        });
      }
      const checks = options.requests.listGithubChecks().filter((check) => check.repo === options.repo);
      return {
        items: checks.map((check) => ({
          id: `github-check:${check.repo}:${check.run_id}`, source: "github-checks",
          project: check.repo.split("/").at(-1), severity: check.conclusion === "failure" ? "warn" : "info",
          kind: "ci", title: `${check.name}: ${check.conclusion ?? check.status}`,
          detail: check.work_key ? `Work: ${check.work_key}` : `No matching request for ${check.sha.slice(0, 12)}`,
          ts: check.completed_at ?? check.started_at ?? check.observed_at, actions: [],
        })),
        panels: [{ id: "github-checks", ts: observedAt, data: { checks } }],
      };
    },
  };
}
