/**
 * Skill-related preferences: resolution, discovery, and formatting.
 *
 * Contains all logic for resolving skill references from preferences
 * to absolute filesystem paths, plus skill discovery and staleness config.
 */
import { existsSync, readdirSync, statSync } from "node:fs";
import { homedir } from "node:os";
import { isAbsolute, join } from "node:path";
import { validatePreferences } from "./preferences-validation.js";
import { sfHome } from "./sf-home.js";
import { SKILLS_DIR } from "./skill-discovery.js";
/**
 * Get skill search directories in priority order for resolution.
 *
 * Searches user and project skill directories (skills.sh and Claude Code),
 * with legacy pre-migration installs as fallback.
 *
 * @param cwd - Current working directory for project-scoped search.
 */
export function getSkillSearchDirs(cwd) {
	const dirs = [
		{ dir: join(cwd, ".agents", "skills"), method: "project-skill" },
		{ dir: SKILLS_DIR, method: "user-skill" },
	];
	// Legacy fallback — read skills from old SF directory only if migration hasn't completed
	const legacyDir = join(sfHome(), "agent", "skills");
	if (
		existsSync(legacyDir) &&
		!existsSync(join(legacyDir, ".migrated-to-agents"))
	) {
		dirs.push({ dir: legacyDir, method: "user-skill" });
	}
	return dirs;
}
/**
 * Resolve a skill reference string to its absolute file path.
 *
 * Tries absolute path (file/dir), then searches known directories for bare name.
 * Returns null path and "unresolved" method if not found.
 *
 * @param ref - Skill reference (absolute path, tilde path, or bare name).
 * @param cwd - Current working directory for project-relative search.
 */
export function resolveSkillReference(ref, cwd) {
	const trimmed = ref.trim();
	// Expand tilde
	const expanded = trimmed.startsWith("~/")
		? join(homedir(), trimmed.slice(2))
		: trimmed;
	// Absolute path
	if (isAbsolute(expanded)) {
		// Direct file reference
		if (existsSync(expanded)) {
			// Check if it's a directory -- look for SKILL.md inside
			try {
				const stat = statSync(expanded);
				if (stat.isDirectory()) {
					const skillFile = join(expanded, "SKILL.md");
					if (existsSync(skillFile)) {
						return {
							original: ref,
							resolvedPath: skillFile,
							method: "absolute-dir",
						};
					}
					return { original: ref, resolvedPath: null, method: "unresolved" };
				}
			} catch {
				/* fall through */
			}
			return { original: ref, resolvedPath: expanded, method: "absolute-path" };
		}
		// Maybe it's a directory path without SKILL.md suffix
		const withSkillMd = join(expanded, "SKILL.md");
		if (existsSync(withSkillMd)) {
			return {
				original: ref,
				resolvedPath: withSkillMd,
				method: "absolute-dir",
			};
		}
		return { original: ref, resolvedPath: null, method: "unresolved" };
	}
	// Bare name -- scan known skill directories
	for (const { dir, method } of getSkillSearchDirs(cwd)) {
		if (!existsSync(dir)) continue;
		try {
			const entries = readdirSync(dir, { withFileTypes: true });
			for (const entry of entries) {
				if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
				if (entry.name === expanded) {
					const skillFile = join(dir, entry.name, "SKILL.md");
					if (existsSync(skillFile)) {
						return { original: ref, resolvedPath: skillFile, method };
					}
				}
			}
		} catch {
			/* directory not readable -- skip */
		}
	}
	return { original: ref, resolvedPath: null, method: "unresolved" };
}
/**
 * Resolve all skill references across a preferences object.
 *
 * Walks all_use_skills, prefer_skills, avoid_skills, and skill rules;
 * caches resolution per reference to avoid redundant directory scans.
 *
 * @param preferences - Preferences object with skill references.
 * @param cwd - Current working directory for project-relative search.
 * @returns Map of resolutions and list of unresolved references.
 */
export function resolveAllSkillReferences(preferences, cwd) {
	const validated = validatePreferences(preferences).preferences;
	preferences = validated;
	const resolutions = new Map();
	const warnings = [];
	function resolve(ref) {
		const existing = resolutions.get(ref);
		if (existing) return existing;
		const result = resolveSkillReference(ref, cwd);
		resolutions.set(ref, result);
		if (result.method === "unresolved") {
			warnings.push(ref);
		}
		return result;
	}
	// Resolve all skill lists
	for (const skill of preferences.always_use_skills ?? []) resolve(skill);
	for (const skill of preferences.prefer_skills ?? []) resolve(skill);
	for (const skill of preferences.avoid_skills ?? []) resolve(skill);
	// Resolve skill rules
	for (const rule of preferences.skill_rules ?? []) {
		for (const skill of rule.use ?? []) resolve(skill);
		for (const skill of rule.prefer ?? []) resolve(skill);
		for (const skill of rule.avoid ?? []) resolve(skill);
	}
	return { resolutions, warnings };
}
// resolveSkillDiscoveryMode and resolveSkillStalenessDays moved to
// preferences.ts to break circular dependency (they need loadEffectiveSFPreferences).
