/**
 * SF Skill Telemetry — Track which skills are loaded per unit (#599)
 *
 * Captures skill names at dispatch time for inclusion in UnitMetrics.
 * Distinguishes between "available" skills (in system prompt) and
 * "actively loaded" skills (read via tool calls during execution).
 *
 * Data flow:
 *   1. At dispatch, captureAvailableSkills() records skills from the system prompt
 *   2. During execution, recordSkillRead() tracks explicit SKILL.md reads
 *   3. At unit completion, getAndClearSkills() returns the loaded list for metrics
 */
import { existsSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { sfHome } from "./sf-home.js";
import { SKILLS_DIR } from "./skill-discovery.js";

// ─── In-memory state ──────────────────────────────────────────────────────────
/** Skills available in the system prompt for the current unit */
let availableSkills = [];
/** Skills explicitly read (SKILL.md loaded) during the current unit */
const activelyLoadedSkills = new Set();
// ─── Public API ───────────────────────────────────────────────────────────────
/**
 * Capture the list of available skill names at dispatch time.
 *
 * Called before each unit starts to establish the baseline for telemetry.
 */
export function captureAvailableSkills() {
	const legacyDir = join(sfHome(), "agent", "skills");
	const names = listSkillNames(SKILLS_DIR);
	// Include skills still in the legacy directory only if migration hasn't completed
	const legacyMigrated = existsSync(join(legacyDir, ".migrated-to-agents"));
	const legacyNames = legacyMigrated ? [] : listSkillNames(legacyDir);
	const all = new Set([...names, ...legacyNames]);
	availableSkills = [...all];
	activelyLoadedSkills.clear();
}
/**
 * Record that a skill was actively loaded during execution.
 *
 * @param skillName - Name of the skill file that was read.
 */
export function recordSkillRead(skillName) {
	activelyLoadedSkills.add(skillName);
}
/**
 * Retrieve captured skills for the current unit and reset state.
 *
 * Returns actively loaded skills if any were read, otherwise returns available skills.
 * Clears all telemetry state after retrieval.
 */
export function getAndClearSkills() {
	const result =
		activelyLoadedSkills.size > 0
			? Array.from(activelyLoadedSkills)
			: [...availableSkills];
	availableSkills = [];
	activelyLoadedSkills.clear();
	return result;
}
/**
 * Clear all telemetry state. Called when autonomous mode stops.
 */
export function resetSkillTelemetry() {
	availableSkills = [];
	activelyLoadedSkills.clear();
}
/**
 * Extract last-used timestamp for each skill from unit metrics.
 *
 * @param units - Array of unit metrics with skill lists and timestamps.
 * @returns Map from skill name to most recent finishedAt timestamp.
 */
export function getSkillLastUsed(units) {
	const lastUsed = new Map();
	for (const u of units) {
		if (!u.skills) continue;
		for (const skill of u.skills) {
			const existing = lastUsed.get(skill) ?? 0;
			if (u.finishedAt > existing) {
				lastUsed.set(skill, u.finishedAt);
			}
		}
	}
	return lastUsed;
}
/**
 * Identify skills unused since a given threshold, including untracked installs.
 *
 * @param units - Unit metrics containing skill usage.
 * @param thresholdDays - Days of inactivity to consider stale.
 * @returns All installed skills exceeding the staleness threshold.
 */
export function detectStaleSkills(units, thresholdDays) {
	if (thresholdDays <= 0) return [];
	const lastUsed = getSkillLastUsed(units);
	const cutoff = Date.now() - thresholdDays * 24 * 60 * 60 * 1000;
	const stale = [];
	// Check all installed skills, not just those with usage data
	const legacyDir = join(sfHome(), "agent", "skills");
	const legacyMigrated = existsSync(join(legacyDir, ".migrated-to-agents"));
	const legacyNames = legacyMigrated ? [] : listSkillNames(legacyDir);
	const installedSet = new Set([...listSkillNames(SKILLS_DIR), ...legacyNames]);
	const installed = [...installedSet];
	for (const skill of installed) {
		const lastTs = lastUsed.get(skill);
		if (lastTs === undefined || lastTs < cutoff) {
			stale.push(skill);
		}
	}
	return stale;
}
// ─── Internals ────────────────────────────────────────────────────────────────
function listSkillNames(skillsDir) {
	if (!existsSync(skillsDir)) return [];
	try {
		return readdirSync(skillsDir, { withFileTypes: true })
			.filter((d) => d.isDirectory() && !d.name.startsWith("."))
			.filter((d) => existsSync(join(skillsDir, d.name, "SKILL.md")))
			.map((d) => d.name);
	} catch {
		return [];
	}
}
