"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = update;
const devkit_1 = require("@nx/devkit");
// Ensure these are installed in your project: npm install ts-morph --save-dev
const ts_morph_1 = require("ts-morph");
const utils_1 = require("../../generators/utils");
/**
 * Finds runtimes that have both runtime-information.ts and runtime-settings.ts files
 * in their src/ or src/lib/ directory.
 * @param tree The file system tree.
 * @returns An array of RuntimeInfo objects.
 */
function findRuntimesWithInformationAndSettingsFiles(tree) {
    const runtimes = new Map();
    const runtimesDir = 'unisphere/runtimes';
    // Only traverse the runtimes directory
    (0, devkit_1.visitNotIgnoredFiles)(tree, runtimesDir, (filePath) => {
        // Look for runtime-information.ts or runtime-settings.ts files in src/ or src/lib/
        const runtimeMatch = filePath.match(/unisphere\/runtimes\/([^\/]+)\/src(?:\/lib)?\/(runtime-information|runtime-settings)\.ts$/);
        if (runtimeMatch) {
            const runtimeName = runtimeMatch[1];
            const fileType = runtimeMatch[2];
            if (!runtimes.has(runtimeName)) {
                runtimes.set(runtimeName, {
                    name: runtimeName,
                    path: `unisphere/runtimes/${runtimeName}`,
                    hasInformation: false,
                    hasSettings: false,
                });
            }
            const runtime = runtimes.get(runtimeName);
            if (fileType === 'runtime-information') {
                runtime.hasInformation = true;
                runtime.informationPath = filePath;
            }
            else if (fileType === 'runtime-settings') {
                runtime.hasSettings = true;
                runtime.settingsPath = filePath;
            }
        }
    });
    let res = Array.from(runtimes.values()).filter((r) => r.hasInformation && r.hasSettings);
    return res;
}
// ------------------------------------------------------------
async function update(tree) {
    devkit_1.logger.info('🔄 Starting runtime info migration to core...');
    const runtimes = findRuntimesWithInformationAndSettingsFiles(tree);
    if (runtimes.length === 0) {
        devkit_1.logger.info('ℹ️ No runtimes found with local runtime-information.ts or runtime-settings.ts files.');
        return;
    }
    devkit_1.logger.info(`📦 Found ${runtimes.length} runtime(s) to migrate`);
    let migratedCount = 0;
    // Initialize the ts-morph Project once
    const project = new ts_morph_1.Project({
        manipulationSettings: {
            quoteKind: ts_morph_1.QuoteKind.Single,
        },
    });
    for (const runtime of runtimes) {
        devkit_1.logger.info(`\n🔄 Migrating runtime: ${runtime.name}`);
        const nameTransforms = (0, utils_1.createNameTransforms)(runtime.name, 'runtimeName');
        const pascalName = nameTransforms['runtimeName__pascalCase'];
        const dashName = nameTransforms['runtimeName__lowerDashCase'];
        const camelName = nameTransforms['runtimeName__camelCase'];
        const corePath = `unisphere/packages/core/src/lib/${runtime.name}-runtime`;
        const runtimeTypesPath = `${corePath}/runtime-types.ts`;
        const indexPath = `${corePath}/index.ts`;
        // 1. Create core runtime-types.ts file
        let settingsContentRaw = runtime.settingsPath && tree.exists(runtime.settingsPath)
            ? tree.read(runtime.settingsPath, 'utf-8') || ''
            : '';
        if (settingsContentRaw) {
            // Rename types in the content to be globally unique
            settingsContentRaw = settingsContentRaw.replace(/\bRuntimeSettings\b/g, `${pascalName}RuntimeSettings`);
            settingsContentRaw = settingsContentRaw.replace(/\bruntimeSettingsSchema\b/g, `${camelName}RuntimeSettingsSchema`);
        }
        // Export the new RuntimeName
        const runtimeTypesContent = `${settingsContentRaw}\n\nexport const ${pascalName}RuntimeName = '${dashName}' as const;`;
        tree.write(runtimeTypesPath, runtimeTypesContent);
        tree.write(indexPath, `export * from './runtime-types';\n`);
        devkit_1.logger.info(`  ✅ Created runtime types in ${corePath}`);
        // 2. Update core index.ts to export the new runtime types
        const coreIndexPath = 'unisphere/packages/core/src/index.ts';
        if (tree.exists(coreIndexPath)) {
            const coreIndexContent = tree.read(coreIndexPath, 'utf-8') || '';
            const exportLine = `export * from './lib/${runtime.name}-runtime';`;
            if (!coreIndexContent.includes(exportLine)) {
                const updatedCoreIndex = coreIndexContent.trimEnd() + '\n' + exportLine + '\n';
                tree.write(coreIndexPath, updatedCoreIndex);
                devkit_1.logger.info(`  🔗 Updated core index exports`);
            }
        }
        else {
            devkit_1.logger.error(`  ⚠️ Core index.ts not found at ${coreIndexPath}`);
        }
        // 3. Update runtime.tsx to import from core alias and rename usages (using ts-morph)
        const runtimeTsxPath = `${runtime.path}/src/lib/runtime.tsx`;
        if (tree.exists(runtimeTsxPath)) {
            try {
                const coreAlias = (0, utils_1.findCorePackageAlias)(tree);
                const runtimeTsxContent = tree.read(runtimeTsxPath, 'utf-8') || '';
                // Create a temporary in-memory SourceFile for ts-morph to parse and modify
                const sourceFile = project.createSourceFile(runtimeTsxPath, runtimeTsxContent, { overwrite: true });
                // Define the new, standardized names
                const newWidgetName = `WidgetName`;
                const newRuntimeName = `${pascalName}RuntimeName`;
                // Define all named imports we are responsible for consolidating/adding
                const newNamedImports = [
                    `${pascalName}RuntimeSettings`,
                    `${camelName}RuntimeSettingsSchema`,
                    newRuntimeName,
                    newWidgetName,
                ];
                // --- A. Remove old local imports ---
                const localImportSpecifiers = [
                    './runtime-information',
                    './runtime-settings',
                    '../runtime-information',
                    '../runtime-settings',
                ];
                sourceFile.getImportDeclarations().forEach(importDeclaration => {
                    const moduleSpecifier = importDeclaration.getModuleSpecifierValue().replace(/\.ts$/, '');
                    if (localImportSpecifiers.includes(moduleSpecifier)) {
                        importDeclaration.remove();
                    }
                });
                // --- B. Rename usages of old identifiers to new, unique names ---
                const identifierRenames = new Map([
                    // Base types/schemas 
                    ['RuntimeSettings', `${pascalName}RuntimeSettings`],
                    ['runtimeSettingsSchema', `${camelName}RuntimeSettingsSchema`],
                    // Constants (Original -> New Core Name)
                    ['unisphereWidgetName', newWidgetName],
                    ['unisphereRuntimeName', newRuntimeName],
                ]);
                // This loop performs the renaming in the file
                sourceFile.forEachDescendant(node => {
                    if (node.isKind(ts_morph_1.SyntaxKind.Identifier)) {
                        const nodeText = node.getText();
                        const newText = identifierRenames.get(nodeText);
                        if (newText) {
                            node.replaceWithText(newText);
                        }
                    }
                });
                // --- C. Add new core imports (SAFELY) ---
                let coreImportDeclaration = sourceFile.getImportDeclaration((decl) => decl.getModuleSpecifierValue() === coreAlias);
                // 1. Remove ONLY the specific named imports we are consolidating from any existing core imports
                sourceFile.getImportDeclarations().forEach(importDeclaration => {
                    const specifier = importDeclaration.getModuleSpecifierValue();
                    // If it's the root core alias, remove only the named imports that we will re-add.
                    if (specifier === coreAlias) {
                        importDeclaration.getNamedImports().forEach(namedImport => {
                            const importName = namedImport.getName();
                            if (newNamedImports.includes(importName)) {
                                namedImport.remove();
                            }
                        });
                        // If the declaration is now empty, remove it completely.
                        if (importDeclaration.getNamedImports().length === 0 && !importDeclaration.getDefaultImport()) {
                            importDeclaration.remove();
                        }
                    }
                });
                // 2. Add the new combined import for runtime types and names
                // If the core import line was removed, we need to recreate it.
                coreImportDeclaration = sourceFile.getImportDeclaration((decl) => decl.getModuleSpecifierValue() === coreAlias);
                if (!coreImportDeclaration) {
                    // If it doesn't exist (i.e., was just deleted or never existed), add a new one.
                    sourceFile.addImportDeclaration({
                        moduleSpecifier: `${coreAlias}`,
                        namedImports: newNamedImports,
                    });
                }
                else {
                    // If it still exists (meaning it had other imports), just add the new ones to the existing declaration.
                    coreImportDeclaration.addNamedImports(newNamedImports);
                }
                // --- D. Write content back to the Tree ---
                // Ensure the tree.write gets the updated text after all replacements
                tree.write(runtimeTsxPath, sourceFile.getFullText());
                devkit_1.logger.info(`  🔁 Updated imports, runtime, and widget usages using ts-morph in ${runtimeTsxPath}`);
            }
            catch (error) {
                devkit_1.logger.error(`  ❌ Failed to update ${runtimeTsxPath} with ts-morph: ${error}`);
            }
        }
        else {
            devkit_1.logger.warn(`  ⚠️ Runtime file not found at ${runtimeTsxPath}`);
        }
        // 4. Delete old files
        if (runtime.informationPath && tree.exists(runtime.informationPath)) {
            tree.delete(runtime.informationPath);
            devkit_1.logger.info(`  🗑️ Deleted ${runtime.informationPath}`);
        }
        if (runtime.settingsPath && tree.exists(runtime.settingsPath)) {
            tree.delete(runtime.settingsPath);
            devkit_1.logger.info(`  🗑️ Deleted ${runtime.settingsPath}`);
        }
        migratedCount++;
        devkit_1.logger.info(`  ✅ Successfully migrated ${runtime.name}`);
    }
    await (0, devkit_1.formatFiles)(tree);
    devkit_1.logger.info('');
    devkit_1.logger.warn('═══════════════════════════════════════════════════════════════');
    devkit_1.logger.warn(`✅ Migrated ${migratedCount} runtime(s) to use core package!`);
    devkit_1.logger.warn('═══════════════════════════════════════════════════════════════');
    devkit_1.logger.warn('');
    devkit_1.logger.warn('⚠️ Summary of changes:');
    devkit_1.logger.warn('  • Runtime settings/information types were moved to core and renamed.');
    devkit_1.logger.warn('  • **unisphereWidgetName was renamed to WidgetName, imported from core, and usages were fixed.**');
    devkit_1.logger.warn('');
    devkit_1.logger.warn('═══════════════════════════════════════════════════════════════');
}
