import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import Ajv from "ajv";
import addFormats from "ajv-formats";

//#region src/cli/commands/plugin/validate/validate-manifest.ts
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const SCHEMAS_DIR = path.join(__dirname, "..", "..", "..", "..", "schemas");
const PLUGIN_MANIFEST_SCHEMA_PATH = path.join(SCHEMAS_DIR, "plugin-manifest.schema.json");
const TEMPLATE_PLUGINS_SCHEMA_PATH = path.join(SCHEMAS_DIR, "template-plugins.schema.json");
const SCHEMA_ID_MAP = {
	"https://databricks.github.io/appkit/schemas/plugin-manifest.schema.json": "plugin-manifest",
	"https://databricks.github.io/appkit/schemas/template-plugins.schema.json": "template-plugins"
};
/**
* Detect which schema type a parsed JSON object targets based on its $schema field.
* Returns "unknown" when the field is missing or unrecognized.
*/
function detectSchemaType(obj) {
	if (!obj || typeof obj !== "object") return "unknown";
	const schemaUrl = obj.$schema;
	if (typeof schemaUrl !== "string") return "unknown";
	return SCHEMA_ID_MAP[schemaUrl] ?? "unknown";
}
let schemaLoadWarned = false;
function loadSchema(schemaPath) {
	try {
		return JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
	} catch (err) {
		if (!schemaLoadWarned) {
			schemaLoadWarned = true;
			console.warn(`Warning: Could not load JSON schema at ${schemaPath}: ${err instanceof Error ? err.message : err}. Falling back to basic validation.`);
		}
		return null;
	}
}
let compiledPluginValidator = null;
function getPluginValidator() {
	if (compiledPluginValidator) return compiledPluginValidator;
	const schema = loadSchema(PLUGIN_MANIFEST_SCHEMA_PATH);
	if (!schema) return null;
	try {
		const ajv = new Ajv({
			allErrors: true,
			strict: false
		});
		addFormats(ajv);
		compiledPluginValidator = ajv.compile(schema);
		return compiledPluginValidator;
	} catch {
		return null;
	}
}
let compiledTemplateValidator = null;
function getTemplateValidator() {
	if (compiledTemplateValidator) return compiledTemplateValidator;
	const pluginSchema = loadSchema(PLUGIN_MANIFEST_SCHEMA_PATH);
	const templateSchema = loadSchema(TEMPLATE_PLUGINS_SCHEMA_PATH);
	if (!pluginSchema || !templateSchema) return null;
	try {
		const ajv = new Ajv({
			allErrors: true,
			strict: false
		});
		addFormats(ajv);
		ajv.addSchema(pluginSchema);
		compiledTemplateValidator = ajv.compile(templateSchema);
		return compiledTemplateValidator;
	} catch {
		return null;
	}
}
/**
* Validate a manifest object against the plugin-manifest JSON schema.
* Returns validation result with optional errors for CLI output.
*/
function validateManifest(obj) {
	if (!obj || typeof obj !== "object") return {
		valid: false,
		errors: [{
			instancePath: "",
			message: "Manifest is not a valid object"
		}]
	};
	const validate = getPluginValidator();
	if (!validate) {
		const m = obj;
		if (typeof m.name === "string" && m.name.length > 0 && typeof m.displayName === "string" && m.displayName.length > 0 && typeof m.description === "string" && m.description.length > 0 && m.resources && typeof m.resources === "object" && Array.isArray(m.resources.required)) return {
			valid: true,
			manifest: obj
		};
		return {
			valid: false,
			errors: [{
				instancePath: "",
				message: "Invalid manifest structure"
			}]
		};
	}
	if (validate(obj)) return {
		valid: true,
		manifest: obj
	};
	return {
		valid: false,
		errors: validate.errors ?? []
	};
}
/**
* Validate a template-plugins manifest (appkit.plugins.json) against its schema.
* Registers the plugin-manifest schema first so external $refs resolve.
*/
function validateTemplateManifest(obj) {
	if (!obj || typeof obj !== "object") return {
		valid: false,
		errors: [{
			instancePath: "",
			message: "Template manifest is not a valid object"
		}]
	};
	const validate = getTemplateValidator();
	if (!validate) {
		const m = obj;
		if (typeof m.version === "string" && m.plugins && typeof m.plugins === "object") return { valid: true };
		return {
			valid: false,
			errors: [{
				instancePath: "",
				message: "Invalid template manifest structure"
			}]
		};
	}
	if (validate(obj)) return { valid: true };
	return {
		valid: false,
		errors: validate.errors ?? []
	};
}
/**
* Convert a JSON pointer like /resources/required/0/permission
* to a readable path like resources.required[0].permission
*/
function humanizePath(instancePath) {
	if (!instancePath) return "(root)";
	return instancePath.replace(/^\//, "").replace(/\/(\d+)\//g, "[$1].").replace(/\/(\d+)$/g, "[$1]").replace(/\//g, ".");
}
/**
* Resolve a JSON pointer to the actual value in the parsed object.
*/
function resolvePointer(obj, instancePath) {
	if (!instancePath) return obj;
	const segments = instancePath.replace(/^\//, "").split("/");
	let current = obj;
	for (const seg of segments) {
		if (current == null || typeof current !== "object") return void 0;
		current = current[seg];
	}
	return current;
}
/**
* Format schema errors for CLI output.
* Collapses anyOf/oneOf sub-errors into a single message and shows
* the actual invalid value when available.
*
* @param errors - AJV error objects
* @param obj - The original parsed object (optional, used to show actual values)
*/
function formatValidationErrors(errors, obj) {
	const grouped = /* @__PURE__ */ new Map();
	for (const e of errors) {
		const key = e.instancePath || "/";
		if (!grouped.has(key)) grouped.set(key, []);
		const list = grouped.get(key);
		if (list) list.push(e);
	}
	const lines = [];
	for (const [path, errs] of grouped) {
		const readable = humanizePath(path);
		const anyOfErr = errs.find((e) => e.keyword === "anyOf" || e.keyword === "oneOf");
		if (anyOfErr) {
			const enumErrors = errs.filter((e) => e.keyword === "enum");
			if (enumErrors.length > 0) {
				const allValues = [...new Set(enumErrors.flatMap((e) => e.params?.allowedValues ?? []))];
				const actual = obj !== void 0 ? resolvePointer(obj, path) : void 0;
				const valueHint = actual !== void 0 ? ` (got ${JSON.stringify(actual)})` : "";
				lines.push(`  ${readable}: invalid value${valueHint}`, `    allowed: ${allValues.join(", ")}`);
				continue;
			}
		}
		for (const e of errs) {
			if (e.keyword === "anyOf" || e.keyword === "oneOf") continue;
			if (e.keyword === "if") continue;
			if (anyOfErr && e.keyword === "enum") continue;
			if (e.keyword === "enum") {
				const allowed = e.params?.allowedValues ?? [];
				const actual = obj !== void 0 ? resolvePointer(obj, path) : void 0;
				const valueHint = actual !== void 0 ? ` (got ${JSON.stringify(actual)})` : "";
				lines.push(`  ${readable}: invalid value${valueHint}, allowed: ${allowed.join(", ")}`);
			} else if (e.keyword === "required") lines.push(`  ${readable}: missing required property "${e.params?.missingProperty}"`);
			else if (e.keyword === "additionalProperties") lines.push(`  ${readable}: unknown property "${e.params?.additionalProperty}"`);
			else if (e.keyword === "pattern") {
				const actual = obj !== void 0 ? resolvePointer(obj, path) : void 0;
				const valueHint = actual !== void 0 ? ` (got ${JSON.stringify(actual)})` : "";
				lines.push(`  ${readable}: does not match expected pattern${valueHint}`);
			} else if (e.keyword === "type") lines.push(`  ${readable}: expected type "${e.params?.type}"`);
			else if (e.keyword === "minLength") lines.push(`  ${readable}: must not be empty`);
			else lines.push(`  ${readable}: ${e.message}${e.params ? ` (${JSON.stringify(e.params)})` : ""}`);
		}
	}
	return lines.join("\n");
}

//#endregion
export { detectSchemaType, formatValidationErrors, validateManifest, validateTemplateManifest };
//# sourceMappingURL=validate-manifest.js.map