import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
import { join, relative, resolve } from "node:path";

const __filename = import.meta.filename;
const __dirname = import.meta.dirname;
const repoRoot = resolve(__dirname, "..");

const featuresPath = join(repoRoot, "FEATURES.md");
const providersPath = join(repoRoot, "packages", "ai", "src", "types.ts");
const extensionsRoot = join(repoRoot, "src", "resources", "extensions");
const sfManifestPath = join(extensionsRoot, "sf", "extension-manifest.json");
const searchProviderPath = resolveExistingPath(
	join(
		repoRoot,
		"src",
		"resources",
		"extensions",
		"search-the-web",
		"provider.ts",
	),
	join(
		repoRoot,
		"src",
		"resources",
		"extensions",
		"search-the-web",
		"provider.js",
	),
);

export const START = "<!-- GENERATED_FEATURE_INVENTORY_START -->";
export const END = "<!-- GENERATED_FEATURE_INVENTORY_END -->";

function uniqueSorted(values) {
	return [...new Set(values)].sort((a, b) => a.localeCompare(b));
}

function resolveExistingPath(...paths) {
	const found = paths.find((path) => existsSync(path));
	if (!found) {
		throw new Error(
			`None of these inventory source paths exist: ${paths.join(", ")}`,
		);
	}
	return found;
}

export function parseKnownProviders() {
	const src = readFileSync(providersPath, "utf8");
	const match = src.match(/export type KnownProvider =([\s\S]*?);/);
	if (!match)
		throw new Error("Could not find KnownProvider in packages/ai/src/types.ts");
	const providers = [...match[1].matchAll(/"([^"]+)"/g)].map((m) => m[1]);
	return uniqueSorted(providers);
}

export function parseBundledExtensions() {
	const entries = readdirSync(extensionsRoot, { withFileTypes: true })
		.filter((entry) => entry.isDirectory())
		.map((entry) => entry.name)
		.filter((name) => {
			try {
				const manifestPath = join(
					extensionsRoot,
					name,
					"extension-manifest.json",
				);
				readFileSync(manifestPath, "utf8");
				return true;
			} catch {
				return false;
			}
		});
	return uniqueSorted(entries);
}

export function parseSfNativeTools() {
	const manifest = JSON.parse(readFileSync(sfManifestPath, "utf8"));
	const tools = manifest?.provides?.tools;
	if (!Array.isArray(tools)) {
		throw new Error(
			"Could not find provides.tools in src/resources/extensions/sf/extension-manifest.json",
		);
	}
	return uniqueSorted(tools.filter((tool) => typeof tool === "string"));
}

export function parseSearchProviders() {
	const src = readFileSync(searchProviderPath, "utf8");
	const preferencesMatch = src.match(
		/const VALID_PREFERENCES = new Set\(\[([\s\S]*?)\]\)/,
	);
	const preferenceProviders = preferencesMatch
		? [...preferencesMatch[1].matchAll(/["']([^"']+)["']/g)].map((m) => m[1])
		: [];
	const providers = [
		...preferenceProviders,
		...src.matchAll(/providers\.push\('([^']+)'\)/g),
		...src.matchAll(/provider\?: '([^']+)'/g),
		...src.matchAll(/\|\s*"([^"]+)"/g),
	]
		.map((m) => (typeof m === "string" ? m : m[1]))
		.filter((p) => p !== "combosearch" && p !== "minimax" && p !== "auto");
	return uniqueSorted(providers);
}

function formatBullets(values, formatter = (value) => `- \`${value}\``) {
	return values.map((value) => formatter(value)).join("\n");
}

export function buildSection() {
	const extensions = parseBundledExtensions();
	const sfNativeTools = parseSfNativeTools();
	const searchProviders = parseSearchProviders();
	const knownProviders = parseKnownProviders();

	return [
		"### SF Native Tools",
		"",
		"Generated from `src/resources/extensions/sf/extension-manifest.json`.",
		"",
		formatBullets(sfNativeTools),
		"",
		"### Bundled Extensions",
		"",
		"Generated from `src/resources/extensions/*/extension-manifest.json`.",
		"",
		formatBullets(
			extensions,
			(value) =>
				`- \`${value}\` — [extension-manifest.json](${relative(repoRoot, join(extensionsRoot, value, "extension-manifest.json"))})`,
		),
		"",
		"### Search Providers",
		"",
		"Generated from the `search-the-web` extension provider declarations.",
		"",
		formatBullets(searchProviders),
		"",
		"### Known Model Providers",
		"",
		"Generated from `packages/ai/src/types.ts` (`KnownProvider`).",
		"",
		formatBullets(knownProviders),
		"",
	].join("\n");
}

export function updateFeaturesContent(features) {
	const startIndex = features.indexOf(START);
	const endIndex = features.indexOf(END);

	if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
		throw new Error("FEATURES.md is missing generated inventory markers");
	}

	const before = features.slice(0, startIndex + START.length);
	const after = features.slice(endIndex);
	const section = `\n\n${buildSection()}`;
	return `${before}${section}\n${after}`;
}

export function main() {
	const features = readFileSync(featuresPath, "utf8");
	const updated = updateFeaturesContent(features);
	writeFileSync(featuresPath, updated);
	process.stdout.write(`Updated ${relative(repoRoot, featuresPath)}\n`);
}

if (process.argv[1] && resolve(process.argv[1]) === __filename) {
	main();
}
