"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppOverrideCSSGenerator = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const foundation_css_1 = require("@wavemaker/foundation-css");
const utils_1 = require("../utils");
class AppOverrideCSSGenerator {
    constructor(projectPath) {
        this.projectPath = projectPath;
        // Function to generate Style Dictionary configuration
        this.getStyleDictionaryConfig = (files, tempFoundationTokensSymlink, mode) => {
            let sourcePattern = "";
            if (mode === "default") {
                // Files without any mode like dark/light in the name
                sourcePattern = `${this.projectPath}/${utils_1.OVERRIDE_TOKENS_PATH}/**/!(*.*).json`;
            }
            else {
                // Files like *.dark.json or *.light.json
                sourcePattern = `${this.projectPath}/${utils_1.OVERRIDE_TOKENS_PATH}/**/*.${mode}.json`;
            }
            return {
                source: [sourcePattern],
                include: [path_1.default.join(tempFoundationTokensSymlink, "**", "!(*.dark|*.light).json")],
                platforms: {
                    css: {
                        transformGroup: "css",
                        prefix: "--wm",
                        files: files
                    },
                },
            };
        };
    }
    processJsonFiles(directory, tokenConfig) {
        const items = fs_extra_1.default.readdirSync(directory);
        items.forEach(item => {
            const itemPath = path_1.default.join(directory, item);
            const stats = fs_extra_1.default.statSync(itemPath);
            if (stats.isDirectory()) {
                // Recursively process subfolders
                this.processJsonFiles(itemPath, tokenConfig);
            }
            else if (stats.isFile() && path_1.default.extname(itemPath) === '.json') {
                tokenConfig.push({
                    sourceFile: itemPath,
                    destinationFile: itemPath.replace('json', 'css')
                });
            }
        });
        return tokenConfig;
    }
    async setupSymlink(from, to) {
        try {
            // Ensure the parent directory of the symlink exists
            await fs_extra_1.default.promises.mkdir(path_1.default.dirname(to), { recursive: true });
            try {
                if (fs_extra_1.default.existsSync(to))
                    await fs_extra_1.default.promises.unlink(to);
            }
            catch (unlinkErr) {
                console.error('Error removing existing symlink:', unlinkErr);
            }
            // Create the symlink
            const isWindows = process.platform === 'win32';
            const symlinkType = isWindows ? 'junction' : 'dir';
            await fs_extra_1.default.promises.symlink(from, to, symlinkType);
            console.log('Symlink created successfully at:', to);
        }
        catch (err) {
            console.error('Error creating symlink:', to, err);
        }
    }
    async removeSymlink(symlinkPath) {
        const resolvedPath = path_1.default.resolve(symlinkPath);
        try {
            const stats = fs_extra_1.default.lstatSync(resolvedPath);
            if (stats.isSymbolicLink()) {
                await fs_extra_1.default.promises.unlink(resolvedPath);
                console.log("Symlink removed successfully:", resolvedPath);
            }
            else {
                console.warn("Not a symlink, skipping unlink:", resolvedPath);
            }
        }
        catch (err) {
            console.error("Error during symlink removal:", err);
        }
    }
    groupFilesByMode(directory) {
        const modeGroups = {
            default: [], // files without a mode in the name
        };
        const items = fs_extra_1.default.readdirSync(directory);
        items.forEach(item => {
            const itemPath = path_1.default.join(directory, item);
            const stats = fs_extra_1.default.statSync(itemPath);
            if (stats.isDirectory()) {
                // Recursively collect from subdirectories
                const subGroup = this.groupFilesByMode(itemPath);
                for (const mode in subGroup) {
                    if (!modeGroups[mode]) {
                        modeGroups[mode] = [];
                    }
                    modeGroups[mode].push(...subGroup[mode]);
                }
            }
            else if (stats.isFile() && path_1.default.extname(itemPath) === '.json') {
                const match = item.match(/\.([^.]+)\.json$/); // Matches ".dark.json" or ".light.json"
                if (match) {
                    const mode = match[1]; // 'dark' or 'light' etc.
                    if (!modeGroups[mode]) {
                        modeGroups[mode] = [];
                    }
                    modeGroups[mode].push(itemPath);
                }
                else {
                    modeGroups.default.push(itemPath);
                }
            }
        });
        return modeGroups;
    }
    ;
    generateModeConfigs(modeGroups, sourceDir, cssVarCalcMixFormatter, componentVariantsFormatter) {
        const baseCssFormatName = (cssVarCalcMixFormatter && cssVarCalcMixFormatter.name) || "css/variables";
        const variantCssFormatName = (componentVariantsFormatter && componentVariantsFormatter.name) || "css/variables";
        const configs = [];
        for (const mode in modeGroups) {
            const tokenFiles = modeGroups[mode];
            const buildPaths = tokenFiles.flatMap((filePath) => {
                const relativePath = filePath.replace(sourceDir + '/', '');
                return [{
                        destination: relativePath.replace(/\.json$/, ".css"),
                        options: { outputReferences: true, selector: ":root" },
                        format: baseCssFormatName,
                        filter: function (token) {
                            var tokenPath;
                            // Extract file path from token['@'].filePath (used in some token formats) or fallback to token.filePath.
                            if (token['@'] && token['@'].filePath) {
                                tokenPath = token['@'].filePath;
                            }
                            else {
                                tokenPath = token.filePath;
                            }
                            // Only include tokens originating from "overrides" files (i.e., filter out foundation CSS tokens)
                            var isOverrideToken = (tokenPath && tokenPath.indexOf("overrides") !== -1) ||
                                (token.filePath && token.filePath.indexOf("overrides") !== -1);
                            // Ensure the token is written only to the correct CSS file
                            var matchesOutputFile = tokenPath && (filePath.includes(tokenPath) || filePath.includes(token.filePath)) && !token.path.includes("appearances") && !token.path.includes("states");
                            return isOverrideToken && matchesOutputFile;
                        }
                    },
                    {
                        destination: relativePath.replace(/\.json$/, "variant.css"),
                        options: { outputReferences: true, selector: ":root" },
                        format: variantCssFormatName,
                        filter: (token) => {
                            let tokenPath;
                            if (token['@'] && token['@'].filePath) {
                                tokenPath = token['@'].filePath;
                            }
                            else {
                                tokenPath = token.filePath;
                            }
                            const isOverrideToken = (tokenPath && tokenPath.indexOf("overrides") !== -1);
                            const matchesOutputFile = tokenPath && (filePath.includes(tokenPath) || filePath.includes(token.filePath)) && (token.path.includes("appearances") || token.path.includes("states"));
                            return isOverrideToken && matchesOutputFile;
                        }
                    }
                ];
            });
            configs.push({
                mode,
                files: buildPaths
            });
        }
        return configs;
    }
    ;
    /**
     * Processes an array of CSS files, merges :root blocks for light mode,
     * and appends others with dynamic attribute selectors.
     * @param {Array<{destination: string}>} files - List of file objects with destination paths.
     * @returns {string} Combined CSS content after processing all files.
     */
    processCSSFiles(files, cssFileContent) {
        for (const { destination } of files) {
            try {
                if (destination.endsWith('.css') && fs_extra_1.default.existsSync(destination)) {
                    const fileContent = fs_extra_1.default.readFileSync(destination, 'utf-8');
                    const rootBlockPattern = /(:root\s*\{[^}]*\})/;
                    const fileName = path_1.default.basename(destination); // Get file name
                    const fileNameArgs = fileName.split('.'); // Split by '.'
                    let modifiedCSS = fileContent;
                    // e.g., color.dark.css → add [color='dark']
                    if (fileNameArgs.length === 3 && fileNameArgs[1] !== 'light') {
                        const attributeName = fileNameArgs[0];
                        const attributeValue = fileNameArgs[1];
                        modifiedCSS = fileContent.replace(/:root/g, `:root[${attributeName}='${attributeValue}']`);
                        cssFileContent += modifiedCSS + '\n';
                    }
                    else {
                        // Merge :root blocks for light/default mode
                        const existingRootMatch = cssFileContent.match(rootBlockPattern);
                        const newRootMatch = fileContent.match(rootBlockPattern);
                        if (existingRootMatch && newRootMatch) {
                            const existingRoot = existingRootMatch[1];
                            const newVariables = newRootMatch[1].replace(/:root\s*\{|\}/g, '').trim();
                            const mergedRoot = existingRoot.replace('}', `\n${newVariables}\n}`);
                            cssFileContent = cssFileContent.replace(existingRoot, mergedRoot);
                        }
                        else {
                            cssFileContent += modifiedCSS + '\n';
                        }
                    }
                    // Delete the processed file
                    fs_extra_1.default.unlink(destination, () => { });
                }
            }
            catch (error) {
                console.error(`\nStyle dictionary, Error processing file ${destination}:`, error);
            }
        }
        return cssFileContent;
    }
    async generate() {
        const sourceDir = `${this.projectPath}`;
        const overridesDir = `${this.projectPath}/themes/overrides`;
        const overrideCssFile = `${this.projectPath}/themes/app.override.css`;
        if (!fs_extra_1.default.existsSync(overridesDir)) {
            return;
        }
        await (async () => {
            const { loadStyleDictionary, loadReferenceUtils } = require('./style-dictionary-wrapper');
            const StyleDictionary = await loadStyleDictionary();
            const foundationPath = (0, utils_1.findPackagePath)(__dirname, '@wavemaker/foundation-css') || '';
            const foundationTokensDir = path_1.default.join(foundationPath, 'src/tokens/mobile/global');
            const { usesReferences, getReferences } = await loadReferenceUtils();
            const cssVarFormatter = (0, foundation_css_1.cssVarCalcMixFormatter)(usesReferences, getReferences);
            const componentVariantFormatter = (0, foundation_css_1.componentVariantsFormatter)(usesReferences, getReferences, 'mobile');
            if (!cssVarFormatter || !componentVariantFormatter) {
                if (!cssVarFormatter) {
                    console.error("Failed to load cssVarCalcMixFormatter.");
                }
                if (!componentVariantFormatter) {
                    console.error("Failed to load componentVariantsFormatter.");
                }
            }
            cssVarFormatter && StyleDictionary.registerFormat(cssVarFormatter);
            componentVariantFormatter && StyleDictionary.registerFormat(componentVariantFormatter);
            const tempFoundationTokensSymlink = `src/main/webapp/${utils_1.DESIGN_TOKENS_DIR_NAME}/temp-tokens`;
            const modeGroups = this.groupFilesByMode(overridesDir);
            const modeConfigs = this.generateModeConfigs(modeGroups, sourceDir, cssVarFormatter, componentVariantFormatter);
            let cssFileContent = '';
            try {
                console.log('\nSetting up foundation tokens symlink...');
                await this.setupSymlink(foundationTokensDir, tempFoundationTokensSymlink);
                for (const { mode, files } of modeConfigs) {
                    console.log(`\nBuilding CSS for mode: ${mode}...`);
                    try {
                        const config = this.getStyleDictionaryConfig(files, tempFoundationTokensSymlink, mode);
                        const sd = new StyleDictionary(config);
                        await sd.buildPlatform("css");
                        console.log(`Style Dictionary build completed for mode: ${mode}`);
                        cssFileContent = this.processCSSFiles(files, cssFileContent);
                        fs_extra_1.default.writeFileSync(overrideCssFile, cssFileContent);
                        console.log(`CSS overrides generated successfully for mode: ${mode}`);
                    }
                    catch (err) {
                        console.error(`Error during Style Dictionary build for mode: ${mode}`, err);
                    }
                }
                console.log('CODEGEN RN APP: generating overrides success !');
            }
            catch (err) {
                console.error('Error setting up symlink or processing modes:', err);
            }
            finally {
                await this.removeSymlink(tempFoundationTokensSymlink);
            }
        })();
    }
}
exports.AppOverrideCSSGenerator = AppOverrideCSSGenerator;
//# sourceMappingURL=app-override-css.generator.js.map