/**
 * profile/parse.mjs — Load + YAML-parse + Zod-validate a single profile file
 * and enumerate the profiles directory.
 *
 * Mirrors `src/cli/wfrun/parse.mjs` in structure: readFileSync → yaml.parse →
 * schema.safeParse → tagged result. Cascade resolution lives separately in
 * `resolve.mjs`; this module intentionally knows nothing about `extends`.
 *
 * Storage convention (locked in spec.md): `.aperant/profiles/{name}.yaml`.
 * The name encoded in the filename must match the `name` field inside the
 * YAML — mismatches are flagged as validation errors.
 */

import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
import { join, resolve as resolvePath } from 'node:path'
import * as YAML from 'yaml'
import { createCoordinationStore } from '../coordination/store.mjs'
import { ProfileSchema, SLUG_RE } from './schema.mjs'

/**
 * Locate `.aperant/profiles/` under the shared root for `projectRoot`.
 * Returns the absolute directory path (may not exist).
 */
function profilesDir(projectRoot) {
	const absRoot = resolvePath(projectRoot || '.')
	const sharedRoot = createCoordinationStore(absRoot).resolveSharedRoot()
	return join(sharedRoot, '.aperant', 'profiles')
}

/**
 * Compute the absolute path for a named profile (without checking existence).
 */
export function profilePath(projectRoot, name) {
	return join(profilesDir(projectRoot), `${name}.yaml`)
}

/**
 * Load + validate a single profile by name.
 *
 * @param {string} projectRoot
 * @param {string} name profile slug (without `.yaml`)
 * @returns {{ok: true, profile: Record<string, any>, path: string}
 *   | {ok: false, errors: {path: string, message: string}[], path: string}}
 */
export function loadProfile(projectRoot, name) {
	// Reject malformed names BEFORE any filesystem touch — a name like
	// "../foo" would otherwise read outside `.aperant/profiles/`. The schema
	// re-validates the in-document `name` field as a defense-in-depth layer.
	if (typeof name !== 'string' || !SLUG_RE.test(name)) {
		const path = profilesDir(projectRoot)
		return {
			ok: false,
			errors: [
				{
					path: 'name',
					message: `invalid profile name "${name}": must match /^[a-z0-9][a-z0-9-]*$/`,
				},
			],
			path,
		}
	}
	const path = profilePath(projectRoot, name)
	if (!existsSync(path)) {
		return {
			ok: false,
			errors: [{ path, message: `profile not found: ${name}` }],
			path,
		}
	}
	let raw
	try {
		raw = readFileSync(path, 'utf-8')
	} catch (err) {
		return {
			ok: false,
			errors: [{ path, message: `cannot read profile: ${err.message}` }],
			path,
		}
	}
	let parsed
	try {
		parsed = YAML.parse(raw)
	} catch (err) {
		return {
			ok: false,
			errors: [{ path, message: `YAML parse error: ${err.message}` }],
			path,
		}
	}
	if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
		return {
			ok: false,
			errors: [{ path, message: 'profile root must be a YAML mapping' }],
			path,
		}
	}
	const result = ProfileSchema.safeParse(parsed)
	if (!result.success) {
		const errors = result.error.issues.map((issue) => ({
			path: issue.path.length === 0 ? '<root>' : issue.path.join('.'),
			message: issue.message,
		}))
		return { ok: false, errors, path }
	}
	const profile = result.data
	// Enforce filename↔name parity so every profile has a single canonical
	// identifier. Without this, `extends: foo` resolution would be ambiguous
	// if foo.yaml declared `name: bar`.
	if (profile.name !== name) {
		return {
			ok: false,
			errors: [
				{
					path: 'name',
					message: `profile name "${profile.name}" does not match filename "${name}.yaml"`,
				},
			],
			path,
		}
	}
	return { ok: true, profile, path }
}

/**
 * Enumerate every `.yaml` file under `.aperant/profiles/` and shallow-validate
 * each one. Returns `[]` if the directory does not exist.
 *
 * Each entry: `{name, extends, path, valid, errors?}`.
 */
export function listProfiles(projectRoot) {
	const dir = profilesDir(projectRoot)
	if (!existsSync(dir)) return []
	let entries
	try {
		entries = readdirSync(dir)
	} catch {
		return []
	}
	const out = []
	for (const entry of entries.sort()) {
		if (!entry.endsWith('.yaml')) continue
		const full = join(dir, entry)
		try {
			if (!statSync(full).isFile()) continue
		} catch {
			continue
		}
		const name = entry.slice(0, -'.yaml'.length)
		const result = loadProfile(projectRoot, name)
		if (result.ok) {
			out.push({
				name: result.profile.name,
				extends: result.profile.extends ?? null,
				path: result.path,
				valid: true,
			})
		} else {
			out.push({
				name,
				extends: null,
				path: result.path,
				valid: false,
				errors: result.errors,
			})
		}
	}
	return out
}

// Re-export for consumers that want to construct paths without re-deriving.
export { profilesDir }
