"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.changeApplicationScopeGenerator = changeApplicationScopeGenerator;
const devkit_1 = require("@nx/devkit");
const generators_1 = require("@nx/workspace/generators");
const utils_1 = require("../utils");
function validateOptions(options) {
    if (!options.applicationName || options.applicationName.trim() === '') {
        throw new Error(`Missing required option: 'applicationName'\n` +
            `  Description: The name of the application to move\n` +
            `  Type: string\n` +
            `  Pattern: ^[a-zA-Z][a-zA-Z0-9\\-\\s]*$\n` +
            `  Example: nx g @unisphere/nx:change-application-scope --applicationName=my-app --newScope=local`);
    }
    const namePattern = /^[a-zA-Z][a-zA-Z0-9\-\s]*$/;
    if (!namePattern.test(options.applicationName)) {
        throw new Error(`Invalid value '${options.applicationName}' for option 'applicationName'\n` +
            `  Pattern: ^[a-zA-Z][a-zA-Z0-9\\-\\s]*$\n` +
            `  Received: ${options.applicationName}`);
    }
    if (!options.newScope) {
        throw new Error(`Missing required option: 'newScope'\n` +
            `  Description: The target subdirectory for the application\n` +
            `  Allowed values: local, server\n` +
            `  Example: nx g @unisphere/nx:change-application-scope --applicationName=my-app --newScope=local`);
    }
    const allowedScopes = ['local', 'server'];
    if (!allowedScopes.includes(options.newScope)) {
        throw new Error(`Invalid value '${options.newScope}' for option 'newScope'\n` +
            `  Allowed values: ${allowedScopes.join(', ')}\n` +
            `  Received: ${options.newScope}`);
    }
}
function findApplicationInUnisphere(tree, applicationName) {
    const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
    const normalizedName = (0, devkit_1.names)(applicationName).fileName;
    if (!unisphereConfig.elements?.applications) {
        throw new Error('No applications found in .unisphere configuration.\n' +
            'Cannot find application to change scope.');
    }
    const appConfig = unisphereConfig.elements.applications[normalizedName];
    if (!appConfig) {
        const available = Object.keys(unisphereConfig.elements.applications);
        throw new Error(`Application "${normalizedName}" not found in .unisphere configuration.\n` +
            'Available applications: ' + (available.length > 0 ? available.join(', ') : '(none)'));
    }
    const sourceRoot = appConfig.sourceRoot;
    if (!sourceRoot) {
        throw new Error(`Application "${normalizedName}" exists but has no sourceRoot configured in .unisphere`);
    }
    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);
    const subdirectory = (0, utils_1.extractApplicationSubdirectory)(sourceRoot);
    if (!subdirectory) {
        throw new Error(`Application "${normalizedName}" has an unexpected sourceRoot format: ${sourceRoot}\n` +
            'Expected format: unisphere/applications/{local|server}/{name}');
    }
    return {
        packageJsonName: packageJson.name,
        sourceRoot,
        subdirectory,
    };
}
function validateDifferentScope(currentSubdirectory, newScope) {
    if (currentSubdirectory === newScope) {
        throw new Error(`Application is already in the "${newScope}" scope.\n` +
            'No changes needed.');
    }
}
function validateNoCollision(tree, applicationName, newSubdirectory) {
    const normalizedName = (0, devkit_1.names)(applicationName).fileName;
    const newPath = `unisphere/applications/${newSubdirectory}/${normalizedName}`;
    if (tree.exists(newPath)) {
        throw new Error(`Directory already exists at ${newPath}.\n` +
            'Please choose a different scope or remove the existing directory first.');
    }
}
function updateUnisphereConfiguration(tree, applicationName, newSourceRoot) {
    const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
    const normalizedName = (0, devkit_1.names)(applicationName).fileName;
    const oldAppConfig = unisphereConfig.elements.applications[normalizedName];
    unisphereConfig.elements.applications[normalizedName] = {
        ...oldAppConfig,
        sourceRoot: newSourceRoot,
    };
    (0, devkit_1.writeJson)(tree, '.unisphere', unisphereConfig);
    devkit_1.logger.info(`✅ Updated .unisphere configuration`);
}
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 updateProjectJson(tree, newPath, oldPath) {
    const projectJsonPath = `${newPath}/project.json`;
    if (!tree.exists(projectJsonPath)) {
        return;
    }
    let content = tree.read(projectJsonPath, 'utf-8');
    if (!content) {
        return;
    }
    if (content.includes(oldPath)) {
        content = content.replace(new RegExp(escapeRegExp(oldPath), 'g'), newPath);
        tree.write(projectJsonPath, content);
        devkit_1.logger.info(`✅ Updated project.json paths`);
    }
}
function updateWebpackConfig(tree, newPath, oldPath) {
    const webpackConfigPath = `${newPath}/webpack.config.js`;
    if (!tree.exists(webpackConfigPath)) {
        return;
    }
    let content = tree.read(webpackConfigPath, 'utf-8');
    if (!content) {
        return;
    }
    if (content.includes(oldPath)) {
        content = content.replace(new RegExp(escapeRegExp(oldPath), 'g'), newPath);
        tree.write(webpackConfigPath, content);
        devkit_1.logger.info(`✅ Updated webpack.config.js references`);
    }
}
function updateReadme(tree, newPath, oldPath) {
    const readmePath = `${newPath}/README.md`;
    if (!tree.exists(readmePath)) {
        return;
    }
    let content = tree.read(readmePath, 'utf-8');
    if (!content) {
        return;
    }
    if (content.includes(oldPath)) {
        content = content.replace(new RegExp(escapeRegExp(oldPath), 'g'), newPath);
        tree.write(readmePath, content);
        devkit_1.logger.info(`✅ Updated README.md`);
    }
}
function escapeRegExp(string) {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
async function changeApplicationScopeGenerator(tree, options) {
    validateOptions(options);
    devkit_1.logger.info('');
    devkit_1.logger.info('🔄 Starting application scope change...');
    devkit_1.logger.info('');
    (0, utils_1.validateUnisphereConfig)(tree);
    const normalizedName = (0, devkit_1.names)(options.applicationName).fileName;
    const appInfo = findApplicationInUnisphere(tree, normalizedName);
    devkit_1.logger.info(`📦 Application: ${normalizedName}`);
    devkit_1.logger.info(`📁 Current location: ${appInfo.sourceRoot} (${appInfo.subdirectory}/)`);
    validateDifferentScope(appInfo.subdirectory, options.newScope);
    const newPath = `unisphere/applications/${options.newScope}/${normalizedName}`;
    validateNoCollision(tree, normalizedName, options.newScope);
    const nxProjectName = `unisphere-application-${normalizedName}`;
    devkit_1.logger.info(`📁 Target location: ${newPath} (${options.newScope}/)`);
    devkit_1.logger.info('');
    devkit_1.logger.info('🔧 Running Nx move generator...');
    try {
        await (0, generators_1.moveGenerator)(tree, {
            projectName: nxProjectName,
            destination: newPath,
            newProjectName: nxProjectName,
            importPath: appInfo.packageJsonName,
            updateImportPath: true,
            skipFormat: false,
        });
        devkit_1.logger.info(`✅ Nx moved project and updated imports`);
    }
    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, normalizedName, newPath);
    updatePackageLock(tree, appInfo.sourceRoot, newPath);
    updateProjectJson(tree, newPath, appInfo.sourceRoot);
    updateWebpackConfig(tree, newPath, appInfo.sourceRoot);
    updateReadme(tree, newPath, appInfo.sourceRoot);
    devkit_1.logger.info('');
    devkit_1.logger.info('✅ Application scope changed successfully!');
    devkit_1.logger.info('');
    devkit_1.logger.info('📋 Summary:');
    devkit_1.logger.info(`  • Application: ${normalizedName}`);
    devkit_1.logger.info(`  • Old scope: ${appInfo.subdirectory}`);
    devkit_1.logger.info(`  • New scope: ${options.newScope}`);
    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 "Change application scope..."');
    devkit_1.logger.info('');
}
exports.default = changeApplicationScopeGenerator;
