/**
 * @license
 * Copyright 2025 HopCode team
 * SPDX-License-Identifier: Apache-2.0
 */
import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { writeStderrLine } from '../utils/stdioHelpers.js';
import { Storage } from '@hoptrendy/hopcode-core';
import { SUPPORTED_LANGUAGES, getLanguageNameFromLocale, resolveSupportedLanguage, } from './languages.js';
export { SUPPORTED_LANGUAGES, getLanguageNameFromLocale, resolveSupportedLanguage };
// State
let currentLanguage = 'en';
let translations = {};
const translationCache = {};
const loadingPromises = {};
// Path helpers
const getBuiltinLocalesDir = () => {
    const __filename = fileURLToPath(import.meta.url);
    return path.join(path.dirname(__filename), 'locales');
};
const getUserLocalesDir = () => path.join(Storage.getGlobalHopCodeDir(), 'locales');
/**
 * Get the path to the user's custom locales directory.
 * Users can place custom language packs (e.g., es.js, fr.js) in this directory.
 * @returns The path to ~/.hopcode/locales
 */
export function getUserLocalesDirectory() {
    return getUserLocalesDir();
}
const getLocalePath = (lang, useUserDir = false) => {
    const baseDir = useUserDir ? getUserLocalesDir() : getBuiltinLocalesDir();
    return path.join(baseDir, `${lang}.js`);
};
// Language detection
export function detectSystemLanguage() {
    const envLang = process.env['HOPCODE_LANG'] || process.env['LANG'];
    if (envLang) {
        // Normalize POSIX locales (e.g. zh_TW.UTF-8 → zh-tw) before matching
        const normalized = envLang.replace(/_/g, '-').toLowerCase();
        for (const lang of SUPPORTED_LANGUAGES) {
            if (normalized.startsWith(lang.code.toLowerCase()))
                return lang.code;
        }
    }
    try {
        const locale = Intl.DateTimeFormat().resolvedOptions().locale;
        const normalized = locale.replace(/_/g, '-').toLowerCase();
        for (const lang of SUPPORTED_LANGUAGES) {
            if (normalized.startsWith(lang.code.toLowerCase()))
                return lang.code;
        }
    }
    catch {
        // Fallback to default
    }
    return 'en';
}
// Translation loading
async function loadTranslationsAsync(lang) {
    if (translationCache[lang]) {
        return translationCache[lang];
    }
    const existingPromise = loadingPromises[lang];
    if (existingPromise) {
        return existingPromise;
    }
    const loadPromise = (async () => {
        // Try user directory first (for custom language packs), then builtin directory
        const searchDirs = [
            { dir: getUserLocalesDir(), isUser: true },
            { dir: getBuiltinLocalesDir(), isUser: false },
        ];
        for (const { dir, isUser } of searchDirs) {
            // Ensure directory exists
            if (!fs.existsSync(dir)) {
                continue;
            }
            const jsPath = getLocalePath(lang, isUser);
            if (!fs.existsSync(jsPath)) {
                continue;
            }
            try {
                // Convert file path to file:// URL for cross-platform compatibility
                const fileUrl = pathToFileURL(jsPath).href;
                try {
                    const module = await import(fileUrl);
                    const result = module.default || module;
                    if (result &&
                        typeof result === 'object' &&
                        Object.keys(result).length > 0) {
                        translationCache[lang] = result;
                        return result;
                    }
                    else {
                        throw new Error('Module loaded but result is empty or invalid');
                    }
                }
                catch {
                    // For builtin locales, try alternative import method (relative path)
                    if (!isUser) {
                        try {
                            const module = await import(`./locales/${lang}.js`);
                            const result = module.default || module;
                            if (result &&
                                typeof result === 'object' &&
                                Object.keys(result).length > 0) {
                                translationCache[lang] = result;
                                return result;
                            }
                        }
                        catch {
                            // Continue to next directory
                        }
                    }
                    // If import failed, continue to next directory
                    continue;
                }
            }
            catch (error) {
                // Log warning but continue to next directory
                if (isUser) {
                    writeStderrLine(`Failed to load translations from user directory for ${lang}: ${error instanceof Error ? error.message : String(error)}`);
                }
                else {
                    writeStderrLine(`Failed to load JS translations for ${lang}: ${error instanceof Error ? error.message : String(error)}`);
                }
                // Continue to next directory
                continue;
            }
        }
        // Final fallback: try the bundled relative import. This works even when
        // the filesystem check fails (e.g., inside a bundled/compiled context
        // where locale files are inlined rather than on disk).
        try {
            const module = await import(`./locales/${lang}.js`);
            const result = module.default || module;
            if (result &&
                typeof result === 'object' &&
                Object.keys(result).length > 0) {
                translationCache[lang] = result;
                return result;
            }
        }
        catch {
            // Bundled import not available either.
        }
        // Return empty object if both directories fail
        // Cache it to avoid repeated failed attempts
        translationCache[lang] = {};
        return {};
    })();
    loadingPromises[lang] = loadPromise;
    // Clean up promise after completion to allow retry on next call if needed
    loadPromise.finally(() => {
        delete loadingPromises[lang];
    });
    return loadPromise;
}
function loadTranslations(lang) {
    // Only return from cache (JS files require async loading)
    return translationCache[lang] || {};
}
// String interpolation
function interpolate(template, params) {
    if (!params)
        return template;
    return template.replace(/\{\{(\w+)\}\}/g, (match, key) => params[key] ?? match);
}
// Language setting helpers
function resolveLanguage(lang) {
    if (lang === 'auto')
        return detectSystemLanguage();
    const resolved = resolveSupportedLanguage(lang);
    return resolved ?? lang;
}
// Public API
export function setLanguage(lang) {
    const resolvedLang = resolveLanguage(lang);
    currentLanguage = resolvedLang;
    // Try to load translations synchronously (from cache only)
    const loaded = loadTranslations(resolvedLang);
    translations = loaded;
    // Warn if translations are empty and JS file exists (requires async loading)
    if (Object.keys(loaded).length === 0) {
        const userJsPath = getLocalePath(resolvedLang, true);
        const builtinJsPath = getLocalePath(resolvedLang, false);
        if (fs.existsSync(userJsPath) || fs.existsSync(builtinJsPath)) {
            writeStderrLine(`Language file for ${resolvedLang} requires async loading. ` +
                `Use setLanguageAsync() instead, or call initializeI18n() first.`);
        }
    }
}
export async function setLanguageAsync(lang) {
    currentLanguage = resolveLanguage(lang);
    translations = await loadTranslationsAsync(currentLanguage);
}
export function getCurrentLanguage() {
    return currentLanguage;
}
export function t(key, params) {
    const translation = translations[key] ?? key;
    if (Array.isArray(translation)) {
        return key;
    }
    return interpolate(translation, params);
}
/**
 * Get a translation that is an array of strings.
 * @param key The translation key
 * @returns The array of strings, or an empty array if not found or not an array
 */
export function ta(key) {
    const translation = translations[key];
    if (Array.isArray(translation)) {
        return translation;
    }
    return [];
}
export async function initializeI18n(lang) {
    await setLanguageAsync(lang ?? 'auto');
}
//# sourceMappingURL=index.js.map