export const REQUIRED_ENV_VARS = [
	"MONGODB_URI",
	"SESSION_SECRET",
	"NAS_ROOT_PATH",
];

export const ALLOWED_NODE_ENVS = new Set(["development", "test", "production"]);
export const MIN_SESSION_SECRET_LENGTH = 32;

const ALLOWED_SEARCH_PROVIDERS = new Set(["fs", "qsirch"]);
const ALLOWED_QSIRCH_DATE_FIELDS = new Set(["modified", "created"]);
const ALLOWED_QSIRCH_MODES = new Set(["sync", "async", "auto"]);

function isBlank(value) {
	return value === undefined || value === null || String(value).trim() === "";
}

function normalizeString(value) {
	return String(value).trim();
}

function normalizeUnixPath(value) {
	let p = normalizeString(value);
	if (p.length > 1) p = p.replace(/\/+$/, "");
	return p;
}

function containsDotDotSegment(p) {
	return /(^|\/)\.\.(\/|$)/.test(p);
}

function looksLikePlaceholderSecret(value) {
	const s = normalizeString(value).toLowerCase();
	return (
		s.includes("change-me") ||
		s.includes("changeme") ||
		s.includes("replace-me") ||
		s.includes("replace_this") ||
		s === "secret" ||
		s === "password"
	);
}

function validateMongoUri(uri) {
	return uri.startsWith("mongodb://") || uri.startsWith("mongodb+srv://");
}

function parsePort(value) {
	const raw = normalizeString(value);
	if (!/^\d+$/.test(raw)) return { ok: false, value: null };
	const n = Number(raw);
	if (!Number.isInteger(n) || n < 1 || n > 65535)
		return { ok: false, value: null };
	return { ok: true, value: n };
}

function buildEnvError(missing, invalid) {
	const lines = [];
	lines.push("Invalid environment configuration.");

	if (missing.length > 0) {
		lines.push("");
		lines.push("Missing required environment variables:");
		for (const key of missing) lines.push(`- ${key}`);
	}

	if (invalid.length > 0) {
		lines.push("");
		lines.push("Invalid environment variables:");
		for (const item of invalid) lines.push(`- ${item.key}: ${item.message}`);
	}

	lines.push("");
	lines.push(
		"Tip: Copy and adjust the example env files (.env.local.example / .env.docker.example)."
	);

	const err = new Error(lines.join("\n"));
	err.code = "ENV_INVALID";
	err.missing = missing;
	err.invalid = invalid;
	return err;
}

function isValidHttpUrl(value) {
	try {
		const u = new URL(value);
		return u.protocol === "http:" || u.protocol === "https:";
	} catch {
		return false;
	}
}

export function validateEnv(env) {
	const e = env ?? {};
	const missing = [];
	const invalid = [];

	for (const key of REQUIRED_ENV_VARS) {
		if (isBlank(e[key])) missing.push(key);
	}

	const mongodbUri = !isBlank(e.MONGODB_URI)
		? normalizeString(e.MONGODB_URI)
		: "";
	if (mongodbUri && !validateMongoUri(mongodbUri)) {
		invalid.push({
			key: "MONGODB_URI",
			message: 'must start with "mongodb://" or "mongodb+srv://"',
		});
	}

	const sessionSecret = !isBlank(e.SESSION_SECRET)
		? normalizeString(e.SESSION_SECRET)
		: "";
	if (sessionSecret) {
		if (sessionSecret.length < MIN_SESSION_SECRET_LENGTH) {
			invalid.push({
				key: "SESSION_SECRET",
				message: `must be at least ${MIN_SESSION_SECRET_LENGTH} characters long`,
			});
		}
		if (looksLikePlaceholderSecret(sessionSecret)) {
			invalid.push({
				key: "SESSION_SECRET",
				message:
					"looks like a placeholder (replace it with a strong random secret)",
			});
		}
	}

	const nasRootPath = !isBlank(e.NAS_ROOT_PATH)
		? normalizeUnixPath(e.NAS_ROOT_PATH)
		: "";
	if (nasRootPath) {
		if (!nasRootPath.startsWith("/")) {
			invalid.push({
				key: "NAS_ROOT_PATH",
				message: 'must be an absolute Unix path (starts with "/")',
			});
		}
		if (containsDotDotSegment(nasRootPath)) {
			invalid.push({
				key: "NAS_ROOT_PATH",
				message: 'must not contain ".." path segments',
			});
		}
	}

	const nodeEnvRaw = !isBlank(e.NODE_ENV)
		? normalizeString(e.NODE_ENV)
		: "development";
	if (nodeEnvRaw && !ALLOWED_NODE_ENVS.has(nodeEnvRaw)) {
		invalid.push({
			key: "NODE_ENV",
			message: 'must be one of "development", "test", "production"',
		});
	}

	const cookieSecureRaw = !isBlank(e.SESSION_COOKIE_SECURE)
		? normalizeString(e.SESSION_COOKIE_SECURE).toLowerCase()
		: "";
	if (
		cookieSecureRaw &&
		cookieSecureRaw !== "true" &&
		cookieSecureRaw !== "false"
	) {
		invalid.push({
			key: "SESSION_COOKIE_SECURE",
			message: 'must be "true" or "false" if provided',
		});
	}

	// -------------------------------------------------------------------------
	// Search provider configuration (RHL-016)
	// -------------------------------------------------------------------------

	const searchProvider = !isBlank(e.SEARCH_PROVIDER)
		? normalizeString(e.SEARCH_PROVIDER).toLowerCase()
		: "fs";

	if (!ALLOWED_SEARCH_PROVIDERS.has(searchProvider)) {
		invalid.push({
			key: "SEARCH_PROVIDER",
			message: 'must be one of "fs" or "qsirch"',
		});
	}

	if (searchProvider === "qsirch") {
		if (isBlank(e.QSIRCH_BASE_URL)) {
			missing.push("QSIRCH_BASE_URL");
		} else if (!isValidHttpUrl(normalizeString(e.QSIRCH_BASE_URL))) {
			invalid.push({
				key: "QSIRCH_BASE_URL",
				message:
					'must be a valid http(s) URL (e.g. "http://192.168.0.22:8080")',
			});
		}

		if (isBlank(e.QSIRCH_ACCOUNT)) missing.push("QSIRCH_ACCOUNT");
		if (isBlank(e.QSIRCH_PASSWORD)) missing.push("QSIRCH_PASSWORD");
		if (isBlank(e.QSIRCH_PATH_PREFIX)) missing.push("QSIRCH_PATH_PREFIX");

		const dateField = !isBlank(e.QSIRCH_DATE_FIELD)
			? normalizeString(e.QSIRCH_DATE_FIELD).toLowerCase()
			: "modified";

		if (!ALLOWED_QSIRCH_DATE_FIELDS.has(dateField)) {
			invalid.push({
				key: "QSIRCH_DATE_FIELD",
				message: 'must be "modified" or "created"',
			});
		}

		const mode = !isBlank(e.QSIRCH_MODE)
			? normalizeString(e.QSIRCH_MODE).toLowerCase()
			: "sync";

		if (!ALLOWED_QSIRCH_MODES.has(mode)) {
			invalid.push({
				key: "QSIRCH_MODE",
				message: 'must be "sync", "async", or "auto"',
			});
		}
	}

	let port;
	if (!isBlank(e.PORT)) {
		const parsed = parsePort(e.PORT);
		if (!parsed.ok) {
			invalid.push({
				key: "PORT",
				message: "must be an integer between 1 and 65535",
			});
		} else {
			port = parsed.value;
		}
	}

	if (missing.length > 0 || invalid.length > 0) {
		throw buildEnvError(missing, invalid);
	}

	const cfg = {
		mongodbUri,
		sessionSecret,
		nasRootPath,
		nodeEnv: nodeEnvRaw,
		searchProvider,
	};

	if (port !== undefined) cfg.port = port;

	return cfg;
}
