/**
 * Auto skill creation flow
 *
 * Purpose: detect repeated repo-specific patterns from journal/activity logs
 * and propose or auto-generate skills when policy allows.
 *
 * Consumer: session startup, turn end, and explicit /skills --auto-create.
 */
import {
	existsSync,
	mkdirSync,
	readdirSync,
	readFileSync,
	writeFileSync,
} from "node:fs";
import { join } from "node:path";
import { getDatabase, getDbPath, openDatabase } from "../sf-db.js";
import { UokCoordinationStore } from "../uok/coordination-store.js";
import { codeSkillTemplate, knowledgeSkillTemplate } from "./templates.js";

const AUTO_SKILL_MIN_OCCURRENCES = 3;
const AUTO_SKILL_COOLDOWN_MS = 24 * 60 * 60 * 1000; // 1 day
const AUTO_SKILL_COOLDOWN_KEY = "skills:auto-create:last-run";

function projectDbPath(basePath) {
	const sfDir = join(basePath, ".sf");
	mkdirSync(sfDir, { recursive: true });
	return join(sfDir, "sf.db");
}

function getCoordinationStore(basePath) {
	const dbPath = projectDbPath(basePath);
	if (!getDatabase() || getDbPath() !== dbPath) {
		openDatabase(dbPath);
	}
	const db = getDatabase();
	if (!db) throw new Error("SF database is not open");
	return new UokCoordinationStore(db);
}

/**
 * Scan activity logs for repeated patterns that could become skills.
 *
 * Purpose: identify recurring file edits, commands, or verification paths
 * that suggest a reusable capability.
 *
 * Consumer: auto skill creation on session startup or turn end.
 */
export function detectSkillCandidates(basePath) {
	const candidates = [];

	// Pattern 1: Repeated file edits on same files
	const fileEdits = scanFileEditPatterns(basePath);
	for (const [file, count] of fileEdits) {
		if (count >= AUTO_SKILL_MIN_OCCURRENCES) {
			candidates.push({
				type: "file-pattern",
				name: `forge-${sanitizeSkillName(file)}`,
				description: `Recurring edits to ${file} (${count} times)`,
				triggerFile: file,
				occurrences: count,
				template: "code",
			});
		}
	}

	// Pattern 2: Repeated bash command patterns
	const commandPatterns = scanCommandPatterns(basePath);
	for (const [pattern, count] of commandPatterns) {
		if (count >= AUTO_SKILL_MIN_OCCURRENCES) {
			candidates.push({
				type: "command-pattern",
				name: `forge-${sanitizeSkillName(pattern)}`,
				description: `Recurring command pattern: ${pattern} (${count} times)`,
				triggerCommand: pattern,
				occurrences: count,
				template: "code",
			});
		}
	}

	// Pattern 3: Repeated verification/check patterns
	const verifyPatterns = scanVerificationPatterns(basePath);
	for (const [pattern, count] of verifyPatterns) {
		if (count >= AUTO_SKILL_MIN_OCCURRENCES) {
			candidates.push({
				type: "verification-pattern",
				name: `forge-verify-${sanitizeSkillName(pattern)}`,
				description: `Recurring verification: ${pattern} (${count} times)`,
				triggerVerify: pattern,
				occurrences: count,
				template: "review",
			});
		}
	}

	return candidates;
}

/**
 * Check if auto-skill creation is allowed by policy.
 */
export function isAutoSkillCreationAllowed(basePath) {
	const data = getCoordinationStore(basePath).get(AUTO_SKILL_COOLDOWN_KEY);
	const lastRun = Number(data?.lastRunMs ?? 0);
	if (lastRun > 0 && Date.now() - lastRun < AUTO_SKILL_COOLDOWN_MS) {
		return false;
	}
	return true;
}

/**
 * Generate a skill from a detected candidate.
 */
export function generateSkill(candidate, basePath) {
	const skillDir = join(basePath, ".agents", "skills", candidate.name);
	if (existsSync(skillDir)) {
		return { created: false, reason: "Skill already exists" };
	}

	let content;
	switch (candidate.template) {
		case "review":
			content = knowledgeSkillTemplate(candidate.name, candidate.description);
			break;
		default:
			content = codeSkillTemplate(candidate.name, candidate.description);
			break;
	}

	// Add auto-generated marker
	content += `\n\n## Auto-Generated Evidence\n\n`;
	content += `- **Type:** ${candidate.type}\n`;
	content += `- **Occurrences:** ${candidate.occurrences}\n`;
	content += `- **Detected:** ${new Date().toISOString()}\n`;
	if (candidate.triggerFile) {
		content += `- **Trigger file:** ${candidate.triggerFile}\n`;
	}
	if (candidate.triggerCommand) {
		content += `- **Trigger command:** ${candidate.triggerCommand}\n`;
	}

	mkdirSync(skillDir, { recursive: true });
	writeFileSync(join(skillDir, "SKILL.md"), content, "utf-8");

	// Update cooldown
	const lastRunMs = Date.now();
	getCoordinationStore(basePath).set(AUTO_SKILL_COOLDOWN_KEY, {
		lastRun: new Date(lastRunMs).toISOString(),
		lastRunMs,
	});

	return { created: true, path: skillDir, name: candidate.name };
}

// ─── Pattern scanners (best-effort heuristics) ────────────────────────────

function scanFileEditPatterns(basePath) {
	const edits = new Map();
	try {
		const activityDir = join(basePath, ".sf", "activity");
		if (!existsSync(activityDir)) return edits;
		const files = readdirSync(activityDir).filter((f) => f.endsWith(".jsonl"));
		for (const file of files.slice(-7)) {
			const raw = readFileSync(join(activityDir, file), "utf-8");
			for (const line of raw.split("\n")) {
				if (!line.trim()) continue;
				try {
					const entry = JSON.parse(line);
					if (entry.toolName === "Write" || entry.toolName === "Edit") {
						const path = entry.input?.path ?? "";
						if (path) {
							const basename = path.split("/").pop() ?? path;
							edits.set(basename, (edits.get(basename) ?? 0) + 1);
						}
					}
				} catch {
					// Skip malformed lines
				}
			}
		}
	} catch {
		// Best-effort scan
	}
	return edits;
}

function scanCommandPatterns(basePath) {
	const commands = new Map();
	try {
		const activityDir = join(basePath, ".sf", "activity");
		if (!existsSync(activityDir)) return commands;
		const files = readdirSync(activityDir).filter((f) => f.endsWith(".jsonl"));
		for (const file of files.slice(-7)) {
			const raw = readFileSync(join(activityDir, file), "utf-8");
			for (const line of raw.split("\n")) {
				if (!line.trim()) continue;
				try {
					const entry = JSON.parse(line);
					if (entry.toolName === "Bash") {
						const cmd = entry.input?.command ?? "";
						// Extract command name (first token)
						const name = cmd.trim().split(/\s+/)[0] ?? "";
						if (name && !name.startsWith("cd") && !name.startsWith("ls")) {
							commands.set(name, (commands.get(name) ?? 0) + 1);
						}
					}
				} catch {
					// Skip malformed lines
				}
			}
		}
	} catch {
		// Best-effort scan
	}
	return commands;
}

function scanVerificationPatterns(basePath) {
	const patterns = new Map();
	try {
		const activityDir = join(basePath, ".sf", "activity");
		if (!existsSync(activityDir)) return patterns;
		const files = readdirSync(activityDir).filter((f) => f.endsWith(".jsonl"));
		for (const file of files.slice(-7)) {
			const raw = readFileSync(join(activityDir, file), "utf-8");
			for (const line of raw.split("\n")) {
				if (!line.trim()) continue;
				try {
					const entry = JSON.parse(line);
					if (entry.toolName === "Bash") {
						const cmd = entry.input?.command ?? "";
						// Look for verification patterns: test, lint, check, verify
						if (
							/\b(npm\s+run\s+(test|lint|check|verify)|npm\s+(test|lint|audit)|vitest|jest|eslint|tsc\s+--noEmit)\b/.test(
								cmd,
							)
						) {
							const pattern =
								cmd.match(
									/\b(npm\s+run\s+\w+|npm\s+\w+|vitest|jest|eslint|tsc)\b/,
								)?.[0] ?? cmd;
							patterns.set(pattern, (patterns.get(pattern) ?? 0) + 1);
						}
					}
				} catch {
					// Skip malformed lines
				}
			}
		}
	} catch {
		// Best-effort scan
	}
	return patterns;
}

function sanitizeSkillName(name) {
	return name
		.toLowerCase()
		.replace(/[^a-z0-9]+/g, "-")
		.replace(/^-|-$/g, "")
		.slice(0, 40);
}
