#!/usr/bin/env node
/**
 * Sends Turbo build stats to the unified QA metrics webhook.
 *
 * Reads the Turbo run summary from .turbo/runs/ and emits per-package
 * build-duration metrics with {package, cache, task} dimensions, plus
 * a run-level build-total-duration summary.
 *
 * Usage: node send-build-stats.mjs
 *
 * Environment variables:
 *   QA_METRICS_WEBHOOK_URL      - Webhook URL (required to send)
 *   QA_METRICS_WEBHOOK_USER     - Basic auth username
 *   QA_METRICS_WEBHOOK_PASSWORD - Basic auth password
 */

import { existsSync, readFileSync, readdirSync } from 'node:fs';
import { join } from 'node:path';

import { sendMetrics, metric } from './send-metrics.mjs';

const runsDir = '.turbo/runs';
if (!existsSync(runsDir)) {
	console.log('No .turbo/runs directory found (turbo --summarize not used), skipping.');
	process.exit(0);
}

const files = readdirSync(runsDir)
	.filter((f) => f.endsWith('.json'))
	.sort();
if (files.length === 0) {
	console.error('No summary file found in .turbo/runs/');
	process.exit(1);
}

const summary = JSON.parse(readFileSync(join(runsDir, files.at(-1)), 'utf-8'));

const metrics = [];

// turbo 2.x emits start/end timestamps rather than a `durationMs` field.
// For cache hits, the execution window measures only restore overhead (~ms),
// so we use `cache.timeSaved` — the duration of the run we avoided — instead.
const elapsedMs = ({ startTime, endTime } = {}) =>
	startTime && endTime ? endTime - startTime : 0;

for (const task of summary.tasks ?? []) {
	if (task.execution?.exitCode !== 0) continue;
	const cacheHit = task.cache?.status === 'HIT';
	const durationMs = cacheHit ? (task.cache.timeSaved ?? 0) : elapsedMs(task.execution);
	// taskId format: "package-name#task-name"
	const [pkg, taskName] = task.taskId?.split('#') ?? [task.package, task.task];

	metrics.push(
		metric('build-duration', durationMs / 1000, 's', {
			package: pkg ?? 'unknown',
			task: taskName ?? 'build',
			cache: cacheHit ? 'hit' : 'miss',
		}),
	);
}

const totalMs = elapsedMs(summary.execution);
const totalTasks = summary.tasks?.length ?? 0;
const cachedTasks = summary.tasks?.filter((t) => t.cache?.status === 'HIT').length ?? 0;

metrics.push(
	metric('build-total-duration', totalMs / 1000, 's', {
		total_tasks: totalTasks,
		cached_tasks: cachedTasks,
	}),
);

// Fire-and-forget: don't await. The in-flight fetch is cancelled on process
// exit, which is the right trade-off — we drop a data point rather than block
// CI on a slow webhook. sendMetrics swallows its own errors, but attach a
// .catch defensively in case that ever changes.
sendMetrics(metrics, 'build-stats').catch((err) =>
	console.warn(`[metrics] send failed: ${err.message}`),
);
