"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.renameApplicationGenerator = renameApplicationGenerator;
const devkit_1 = require("@nx/devkit");
require('chalk').__esModule = true;
const generators_1 = require("@nx/workspace/generators");
const utils_1 = require("../utils");
function validateOptions(options) {
    if (!options.oldApplicationName || options.oldApplicationName.trim() === '') {
        throw new Error(`Missing required option: 'oldApplicationName'\n` +
            `  Description: The current name of the application to rename\n` +
            `  Type: string\n` +
            `  Example: nx g @unisphere/nx:rename-application --oldApplicationName=old-name --newApplicationName=new-name`);
    }
    if (!options.newApplicationName || options.newApplicationName.trim() === '') {
        throw new Error(`Missing required option: 'newApplicationName'\n` +
            `  Description: The new name for the application\n` +
            `  Type: string\n` +
            `  Pattern: ^[a-zA-Z][a-zA-Z0-9\\-\\s]*$\n` +
            `  Example: nx g @unisphere/nx:rename-application --oldApplicationName=old-name --newApplicationName=new-name`);
    }
    const namePattern = /^[a-zA-Z][a-zA-Z0-9\-\s]*$/;
    if (!namePattern.test(options.newApplicationName)) {
        throw new Error(`Invalid value '${options.newApplicationName}' for option 'newApplicationName'\n` +
            `  Pattern: ^[a-zA-Z][a-zA-Z0-9\\-\\s]*$\n` +
            `  Received: ${options.newApplicationName}`);
    }
}
function validateOldApplicationExists(tree, oldApplicationName) {
    const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
    const normalizedOldName = (0, devkit_1.names)(oldApplicationName).fileName;
    if (!unisphereConfig.elements?.applications?.[normalizedOldName]) {
        const available = Object.keys(unisphereConfig.elements?.applications || {});
        throw new Error(`Application "${normalizedOldName}" not found in .unisphere configuration.\n` +
            'Available applications: ' + (available.length > 0 ? available.join(', ') : '(none)'));
    }
    const appConfig = unisphereConfig.elements.applications[normalizedOldName];
    const sourceRoot = appConfig.sourceRoot;
    if (!sourceRoot) {
        throw new Error(`Application "${normalizedOldName}" exists but has no sourceRoot configured in .unisphere`);
    }
    const subdirectory = (0, utils_1.extractApplicationSubdirectory)(sourceRoot);
    if (!subdirectory) {
        throw new Error(`Application "${normalizedOldName}" has an unexpected sourceRoot format: ${sourceRoot}\n` +
            'Expected format: unisphere/applications/{local|server}/{name}');
    }
    if (!tree.exists(sourceRoot)) {
        throw new Error(`Application directory not found at ${sourceRoot}.\n` +
            'The .unisphere configuration references an application that does not exist.');
    }
    const packageJsonPath = `${sourceRoot}/package.json`;
    if (!tree.exists(packageJsonPath)) {
        throw new Error(`package.json not found at ${packageJsonPath}.\n` +
            'The application directory exists but is missing package.json.');
    }
    const packageJson = (0, devkit_1.readJson)(tree, packageJsonPath);
    return {
        packageJsonName: packageJson.name,
        sourceRoot,
        subdirectory,
        oldPath: sourceRoot,
    };
}
function validateNewApplicationDoesNotExist(tree, newApplicationName, subdirectory) {
    const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
    const normalizedNewName = (0, devkit_1.names)(newApplicationName).fileName;
    if (unisphereConfig.elements?.applications?.[normalizedNewName]) {
        throw new Error(`Application "${normalizedNewName}" already exists in .unisphere configuration.\n` +
            'Please choose a different application name.');
    }
    const newPath = `unisphere/applications/${subdirectory}/${normalizedNewName}`;
    if (tree.exists(newPath)) {
        throw new Error(`Application directory already exists at ${newPath}.\n` +
            'Please choose a different application name or remove the existing directory.');
    }
}
function updateUnisphereConfiguration(tree, oldApplicationName, newApplicationName, appInfo) {
    const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
    const normalizedNewName = (0, devkit_1.names)(newApplicationName).fileName;
    const normalizedOldName = (0, devkit_1.names)(oldApplicationName).fileName;
    const newSourceRoot = `unisphere/applications/${appInfo.subdirectory}/${normalizedNewName}`;
    const newApplications = {};
    Object.keys(unisphereConfig.elements.applications).forEach((key) => {
        if (key === normalizedOldName) {
            newApplications[normalizedNewName] = {
                ...unisphereConfig.elements.applications[normalizedOldName],
                sourceRoot: newSourceRoot,
            };
        }
        else {
            newApplications[key] = unisphereConfig.elements.applications[key];
        }
    });
    unisphereConfig.elements.applications = newApplications;
    (0, devkit_1.writeJson)(tree, '.unisphere', unisphereConfig);
    devkit_1.logger.info(`✅ Updated .unisphere configuration`);
}
function updatePackageJson(tree, packagePath, newPackageJsonName) {
    const packageJsonPath = `${packagePath}/package.json`;
    if (!tree.exists(packageJsonPath)) {
        devkit_1.logger.warn(`⚠️  package.json not found at ${packageJsonPath}`);
        return;
    }
    const packageJson = (0, devkit_1.readJson)(tree, packageJsonPath);
    if (packageJson.name !== newPackageJsonName) {
        packageJson.name = newPackageJsonName;
        (0, devkit_1.writeJson)(tree, packageJsonPath, packageJson);
        devkit_1.logger.info(`✅ Updated package.json name to "${newPackageJsonName}"`);
    }
}
function updatePackageLock(tree, oldPath, newPath) {
    const packageLockPath = 'package-lock.json';
    if (!tree.exists(packageLockPath)) {
        return;
    }
    const packageLock = (0, devkit_1.readJson)(tree, packageLockPath);
    let updated = false;
    if (packageLock.packages) {
        const packagesEntries = Object.entries(packageLock.packages);
        const newPackages = {};
        for (const [key, value] of packagesEntries) {
            if (key === oldPath) {
                newPackages[newPath] = value;
                updated = true;
            }
            else {
                newPackages[key] = value;
            }
        }
        packageLock.packages = newPackages;
    }
    if (packageLock.packages) {
        for (const [, value] of Object.entries(packageLock.packages)) {
            if (value && typeof value === 'object' && 'resolved' in value) {
                const pkg = value;
                if (pkg.resolved === oldPath) {
                    pkg.resolved = newPath;
                    updated = true;
                }
            }
        }
    }
    if (updated) {
        (0, devkit_1.writeJson)(tree, packageLockPath, packageLock);
        devkit_1.logger.info(`✅ Updated package-lock.json path keys`);
    }
}
function updateWebpackConfig(tree, newPath, oldNormalizedName, newNormalizedName) {
    const webpackConfigPath = `${newPath}/webpack.config.js`;
    if (!tree.exists(webpackConfigPath)) {
        return;
    }
    let content = tree.read(webpackConfigPath, 'utf-8');
    if (!content) {
        return;
    }
    const oldPattern = `application-${oldNormalizedName}`;
    const newPattern = `application-${newNormalizedName}`;
    if (content.includes(oldPattern)) {
        content = content.replace(new RegExp(escapeRegExp(oldPattern), 'g'), newPattern);
        tree.write(webpackConfigPath, content);
        devkit_1.logger.info(`✅ Updated webpack.config.js references`);
    }
}
function updateProjectJson(tree, newPath, oldPath, oldNxProjectName, newNxProjectName) {
    const projectJsonPath = `${newPath}/project.json`;
    if (!tree.exists(projectJsonPath)) {
        return;
    }
    let content = tree.read(projectJsonPath, 'utf-8');
    if (!content) {
        return;
    }
    let updated = false;
    if (content.includes(oldPath) && oldPath !== newPath) {
        const result = safeReplace(content, oldPath, newPath);
        if (result !== content) {
            content = result;
            updated = true;
        }
    }
    if (content.includes(oldNxProjectName) && oldNxProjectName !== newNxProjectName) {
        const result = safeReplace(content, oldNxProjectName, newNxProjectName);
        if (result !== content) {
            content = result;
            updated = true;
        }
    }
    if (updated) {
        tree.write(projectJsonPath, content);
        devkit_1.logger.info(`✅ Updated project.json paths and comments`);
    }
}
function safeReplace(text, oldVal, newVal) {
    if (oldVal === newVal)
        return text;
    if (!newVal.includes(oldVal)) {
        return text.replace(new RegExp(escapeRegExp(oldVal), 'g'), newVal);
    }
    const placeholder = `\x00__SAFE_REPLACE_${Date.now()}__\x00`;
    let result = text.replace(new RegExp(escapeRegExp(newVal), 'g'), placeholder);
    result = result.replace(new RegExp(escapeRegExp(oldVal), 'g'), newVal);
    result = result.replace(new RegExp(escapeRegExp(placeholder), 'g'), newVal);
    return result;
}
function updateReadme(tree, newPath, oldNormalizedName, newNormalizedName) {
    const readmePath = `${newPath}/README.md`;
    if (!tree.exists(readmePath)) {
        return;
    }
    let content = tree.read(readmePath, 'utf-8');
    if (!content) {
        return;
    }
    let updated = false;
    const toHumanReadable = (name) => name
        .replace(/[-_]/g, ' ')
        .replace(/\b\w/g, (l) => l.toUpperCase());
    const oldHumanReadable = toHumanReadable(oldNormalizedName);
    const newHumanReadable = toHumanReadable(newNormalizedName);
    if (content.includes(oldHumanReadable)) {
        content = content.replace(new RegExp(escapeRegExp(oldHumanReadable), 'g'), newHumanReadable);
        updated = true;
    }
    if (content.includes(oldNormalizedName)) {
        content = content.replace(new RegExp(escapeRegExp(oldNormalizedName), 'g'), newNormalizedName);
        updated = true;
    }
    if (updated) {
        tree.write(readmePath, content);
        devkit_1.logger.info(`✅ Updated README.md`);
    }
}
function updateRootServeScript(tree, oldNormalizedName, newNormalizedName) {
    if (!tree.exists('package.json')) {
        return;
    }
    const packageJson = (0, devkit_1.readJson)(tree, 'package.json');
    const oldScriptName = `serve:${oldNormalizedName}`;
    if (!packageJson.scripts?.[oldScriptName]) {
        return;
    }
    const oldCommand = packageJson.scripts[oldScriptName];
    const newCommand = oldCommand.replace(new RegExp(escapeRegExp(oldNormalizedName), 'g'), newNormalizedName);
    const newScriptName = `serve:${newNormalizedName}`;
    (0, utils_1.renameScriptInRootPackageJson)(tree, oldScriptName, newScriptName, newCommand);
}
function escapeRegExp(string) {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
async function renameApplicationGenerator(tree, options) {
    validateOptions(options);
    devkit_1.logger.info('');
    devkit_1.logger.info('🔄 Starting application rename...');
    devkit_1.logger.info('');
    (0, utils_1.validateUnisphereConfig)(tree);
    const normalizedOldName = (0, devkit_1.names)(options.oldApplicationName).fileName;
    const normalizedNewName = (0, devkit_1.names)(options.newApplicationName).fileName;
    const appInfo = validateOldApplicationExists(tree, normalizedOldName);
    validateNewApplicationDoesNotExist(tree, normalizedNewName, appInfo.subdirectory);
    const oldNxProjectName = `unisphere-application-${normalizedOldName}`;
    const newNxProjectName = `unisphere-application-${normalizedNewName}`;
    const newPackageJsonName = `unisphere-application-${normalizedNewName}`;
    const newPath = `unisphere/applications/${appInfo.subdirectory}/${normalizedNewName}`;
    devkit_1.logger.info(`📦 Old application name: ${normalizedOldName}`);
    devkit_1.logger.info(`📦 New application name: ${normalizedNewName}`);
    devkit_1.logger.info(`🏷️  Nx project name: ${oldNxProjectName} → ${newNxProjectName}`);
    devkit_1.logger.info('');
    devkit_1.logger.info('🔧 Running Nx move generator...');
    try {
        await (0, generators_1.moveGenerator)(tree, {
            projectName: oldNxProjectName,
            destination: newPath,
            newProjectName: newNxProjectName,
            importPath: newPackageJsonName,
            updateImportPath: true,
            skipFormat: false,
        });
        devkit_1.logger.info(`✅ Nx moved project from ${appInfo.oldPath} to ${newPath}`);
    }
    catch (error) {
        devkit_1.logger.error(`❌ Failed to move project with Nx: ${error}`);
        throw error;
    }
    devkit_1.logger.info('');
    devkit_1.logger.info('🧹 Performing Unisphere-specific cleanup...');
    updateUnisphereConfiguration(tree, normalizedOldName, normalizedNewName, appInfo);
    updatePackageJson(tree, newPath, newPackageJsonName);
    updatePackageLock(tree, appInfo.oldPath, newPath);
    updateWebpackConfig(tree, newPath, normalizedOldName, normalizedNewName);
    updateProjectJson(tree, newPath, appInfo.oldPath, oldNxProjectName, newNxProjectName);
    updateReadme(tree, newPath, normalizedOldName, normalizedNewName);
    updateRootServeScript(tree, normalizedOldName, normalizedNewName);
    devkit_1.logger.info('');
    devkit_1.logger.info('✅ Application renamed successfully!');
    devkit_1.logger.info('');
    devkit_1.logger.info('📋 Summary:');
    devkit_1.logger.info(`  • Old name: ${normalizedOldName}`);
    devkit_1.logger.info(`  • New name: ${normalizedNewName}`);
    devkit_1.logger.info(`  • Old location: ${appInfo.sourceRoot}`);
    devkit_1.logger.info(`  • New location: ${newPath}`);
    devkit_1.logger.info('');
    devkit_1.logger.info('📝 Next steps:');
    devkit_1.logger.info('  1. Review the changes: git status');
    devkit_1.logger.info('  2. Stage all changes: git add -A');
    devkit_1.logger.info('     Git will detect the directory move as a rename (preserving history)');
    devkit_1.logger.info('  3. Run: npm install (to update package-lock.json)');
    devkit_1.logger.info('  4. Run: npm run build (to verify everything builds)');
    devkit_1.logger.info('  5. Commit the changes: git commit -m "Rename application..."');
    devkit_1.logger.info('');
}
exports.default = renameApplicationGenerator;
