/**
 * This file is responsible for managing translation files.
 *
 * It provides utilities to work with translation keys and their corresponding
 * file paths.
 *
 * Note: The exported function is imported and used by the main index of the integration,
 * to generate the list of available translation keys and inject them into a server/client
 * friendly Vite virtual module.
 *
 * If you are interested in contributing to the translation effort, please visit our Crowdin project:
 * https://crowdin.com/project/studiocms or submit a pull request to the `translations` folder:
 * `packages/studiocms/src/virtuals/i18n/translations/` on https://github.com/withstudiocms/studiocms
 */

import { posix } from 'node:path';
import { fileURLToPath } from 'node:url';
import { readJson } from '@withstudiocms/internal_helpers/utils';
import { glob } from 'tinyglobby';
import type {
	LanguageFlagIdentifier,
	ServerUiTranslations,
	StudioCMSTranslationRecord,
} from './config.js';
import { translationFlagKeyOverrides } from './overrides.js';

/**
 * The directory containing translation files.
 */
const translationsDir = fileURLToPath(new URL('./translations/', import.meta.url));

/**
 * Asynchronously retrieves a list of available translation JSON files from the
 * `translations` directory relative to the current module.
 *
 * @remarks
 * Uses the `glob` function to match all `.json` files recursively within the
 * `translations` directory. The directory path is resolved using `import.meta.url`.
 *
 * @example
 * // Example result:
 * // ['en.json', 'fr.json', 'nested/de.json']
 *
 * @returns {Promise<string[]>} A promise that resolves to an array of relative file paths
 * of translation files.
 */
const availableTranslationFiles = await glob('**/*.json', { cwd: translationsDir });

/**
 * An array of translation file keys derived from `availableTranslationFiles`.
 * Each key is generated by removing the `.json` extension from the file name.
 * Falsy values are filtered out from the resulting array.
 *
 * @example
 * // If availableTranslationFiles = ['en.json', 'fr.json']
 * // availableTranslationFileKeys = ['en', 'fr']
 */
export const availableTranslationFileKeys = availableTranslationFiles
	.map((file) => posix.normalize(file).replace(/\.json$/, ''))
	.filter(Boolean);

/**
 * Recursively checks a record object for string values and counts the total number of strings
 * and the number of empty strings (strings that are empty or contain only whitespace).
 *
 * @param record - The object to check, which may contain nested objects and string values.
 * @returns An object containing:
 *   - `count`: The total number of string values found.
 *   - `emptyStrings`: The number of string values that are empty or contain only whitespace.
 */
function checkStrings(record: unknown): { count: number; emptyStrings: number } {
	if (typeof record !== 'object' || record === null) return { count: 0, emptyStrings: 0 };
	let count = 0;
	let emptyStrings = 0;
	for (const value of Object.values(record as Record<string, unknown>)) {
		if (typeof value === 'string') {
			count++;
			if (value.trim() === '') emptyStrings++;
		} else if (typeof value === 'object' && value !== null) {
			const { count: c, emptyStrings: e } = checkStrings(value);
			count += c;
			emptyStrings += e;
		}
	}
	return { count, emptyStrings };
}

const MISSING_RATIO_THRESHOLD = 0.1 as const; // 10%

/**
 * Checks whether the ratio of empty strings to total strings in a translation file
 * is within an acceptable threshold.
 *
 * @param opt - An object containing:
 *   - `count`: The total number of strings in the translation file.
 *   - `emptyStrings`: The number of empty strings in the translation file.
 * @returns `true` if the ratio of empty strings is less than or equal to 10%, otherwise `false`.
 */
function checkThreshold(opt: { count: number; emptyStrings: number }) {
	const threshold = MISSING_RATIO_THRESHOLD;
	if (opt.count === 0) return false;
	return opt.emptyStrings / opt.count <= threshold;
}

/**
 * Loads and returns the available server-side UI translations, excluding English ('en').
 * For each translation file key, reads the corresponding JSON file, checks its string values,
 * and includes it in the results only if it passes the threshold check.
 * Skips translation files with too many empty strings and logs a warning.
 *
 * @returns {ServerUiTranslations} An object mapping language keys to their translation records.
 */
export const availableTranslations: ServerUiTranslations = (() => {
	const results: ServerUiTranslations = {};

	const translationKeys = availableTranslationFileKeys.filter((key) => key !== 'en');

	for (const key of translationKeys) {
		const translation = readJson<StudioCMSTranslationRecord>(`${translationsDir + key}.json`);

		const result = checkStrings(translation);

		if (!checkThreshold(result)) {
			continue;
		}

		results[key] = translation;
	}

	return results;
})();

/**
 * Restored Available Translations which includes the default
 * english translation key for usage generating the language flags
 */
export const availableTranslationsKeys = ['en', ...Object.keys(availableTranslations)];

/**
 * An array of language flags derived from `availableTranslationsKeys`.
 * Each flag is generated by mapping the key to a corresponding icon from
 * the `@iconify-json/circle-flags` package. And allows overriding default flags
 * using the `translationFlagKeyOverrides`.
 *
 * @example
 * // If availableTranslationsKeys = ['en', 'fr']
 * // langFlags = [{ key: 'en', flag: 'lang-en-us' }, { key: 'fr', flag: 'lang-fr' }]
 */
export const currentFlags: Array<{ key: string; flag: LanguageFlagIdentifier }> =
	availableTranslationsKeys.map((key) => {
		const flagKey: LanguageFlagIdentifier = translationFlagKeyOverrides[key] || `lang-${key}`;
		return { key, flag: flagKey };
	});
