"use strict";
/**
 * Change Package Scope Generator
 *
 * Changes a package's npm scope by:
 * 1. Validating the package exists and target scope is valid
 * 2. Parsing the package.json name to extract current scope and package name
 * 3. Using Nx's built-in move generator to handle:
 *    - Directory and file moves
 *    - Import statement updates across the workspace
 *    - TypeScript path mappings in tsconfig.base.json
 * 4. Performing Unisphere-specific cleanup:
 *    - Updating .unisphere configuration
 *    - Ensuring package.json name matches conventions
 *    - Updating package-lock.json path keys
 *    - Updating vite.config.ts cache directory paths
 *    - Updating project.json lintFilePatterns and comments
 *    - Updating README.md with new package name
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.changePackageScopeGenerator = changePackageScopeGenerator;
const devkit_1 = require("@nx/devkit");
const generators_1 = require("@nx/workspace/generators");
const utils_1 = require("../utils");
const utils_2 = require("./utils");
function validateOptions(options) {
    if (!options.packageJsonName || options.packageJsonName.trim() === '') {
        throw new Error(`Missing required option: 'packageJsonName'\n` +
            `  Description: The full package.json name\n` +
            `  Type: string\n` +
            `  Pattern: ^@[a-z0-9-]+/[a-z0-9-]+$\n` +
            `  Example: nx g @unisphere/nx:change-package-scope --packageJsonName=@kaltura-corp/unisphere-my-package --newScope=kaltura-sdk`);
    }
    const namePattern = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
    if (!namePattern.test(options.packageJsonName)) {
        throw new Error(`Invalid value '${options.packageJsonName}' for option 'packageJsonName'\n` +
            `  Pattern: ^@[a-z0-9-]+/[a-z0-9-]+$\n` +
            `  Received: ${options.packageJsonName}`);
    }
    if (!options.newScope) {
        throw new Error(`Missing required option: 'newScope'\n` +
            `  Description: The target scope for the package\n` +
            `  Allowed values: unisphere, kaltura-corp, kaltura-sdk, kaltura-apps, kaltura-ui, local\n` +
            `  Example: nx g @unisphere/nx:change-package-scope --packageJsonName=@kaltura-corp/unisphere-my-package --newScope=kaltura-sdk`);
    }
    const allowedScopes = ['unisphere', 'kaltura-corp', 'kaltura-sdk', 'kaltura-apps', 'kaltura-ui', 'local'];
    if (!allowedScopes.includes(options.newScope)) {
        throw new Error(`Invalid value '${options.newScope}' for option 'newScope'\n` +
            `  Allowed values: ${allowedScopes.join(', ')}\n` +
            `  Received: ${options.newScope}`);
    }
}
/**
 * Finds a package in .unisphere config by matching the package.json name.
 * Extracts package information needed for scope change.
 */
function findPackageInUnisphere(tree, packageJsonName, experienceName) {
    const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
    if (!unisphereConfig.elements?.packages) {
        throw new Error('No packages found in .unisphere configuration.\n' +
            'Cannot find package to change scope.');
    }
    // Parse the provided package.json name to get package identifier
    const { packageName } = (0, utils_2.parsePackageJsonName)(packageJsonName, experienceName);
    // Find the package by identifier in .unisphere
    const packageConfig = unisphereConfig.elements.packages[packageName];
    if (!packageConfig) {
        throw new Error(`Package with identifier "${packageName}" not found in .unisphere configuration.\n` +
            'Available packages: ' +
            Object.keys(unisphereConfig.elements.packages).join(', '));
    }
    const sourceRoot = packageConfig.sourceRoot;
    if (!sourceRoot) {
        throw new Error(`Package "${packageName}" exists but has no sourceRoot configured in .unisphere`);
    }
    // Verify the directory exists
    if (!tree.exists(sourceRoot)) {
        throw new Error(`Package directory not found at ${sourceRoot}.\n` +
            'The .unisphere configuration references a package that does not exist.');
    }
    // Read the package.json to verify the name matches
    const packageJsonPath = `${sourceRoot}/package.json`;
    if (!tree.exists(packageJsonPath)) {
        throw new Error(`package.json not found at ${packageJsonPath}.\n` +
            'The package directory exists but is missing package.json.');
    }
    const packageJson = (0, devkit_1.readJson)(tree, packageJsonPath);
    if (packageJson.name !== packageJsonName) {
        throw new Error(`Package name mismatch!\n` +
            `Expected: ${packageJsonName}\n` +
            `Found in package.json: ${packageJson.name}\n` +
            `Location: ${packageJsonPath}`);
    }
    const subdirectory = (0, utils_1.extractSubdirectoryFromSourceRoot)(sourceRoot);
    const currentScope = (0, utils_2.extractScopeFromSubdirectory)(subdirectory);
    return {
        packageIdentifier: packageName,
        packageJsonName,
        currentScope,
        sourceRoot,
        subdirectory,
    };
}
/**
 * Validates that the target scope is different from current scope.
 */
function validateDifferentScope(currentScope, newScope) {
    if (currentScope === newScope) {
        throw new Error(`Package is already in the target scope "${newScope}".\n` +
            'No changes needed.');
    }
}
/**
 * Validates that the target subdirectory doesn't already have a package with this identifier.
 */
function validateNoCollision(tree, packageIdentifier, newSubdirectory) {
    // Check if directory exists on filesystem
    const newPath = `unisphere/packages/${newSubdirectory}/${packageIdentifier}`;
    if (tree.exists(newPath)) {
        throw new Error(`Directory already exists at ${newPath}.\n` +
            'Please choose a different scope or remove the existing directory first.');
    }
}
/**
 * Validates special package restrictions (e.g., types package scope rules).
 */
function validateSpecialPackageRestrictions(packageIdentifier, newScope) {
    if (packageIdentifier === 'types') {
        const allowedScopes = ['@unisphere', '@kaltura-corp'];
        if (!allowedScopes.includes(newScope)) {
            throw new Error(`The "types" package can only exist in @unisphere or @kaltura-corp scopes.\n` +
                `You attempted to move it to ${newScope}.\n` +
                `Allowed scopes: ${allowedScopes.join(', ')}`);
        }
    }
}
/**
 * Update .unisphere configuration with the new sourceRoot.
 */
function updateUnisphereConfiguration(tree, packageIdentifier, newSourceRoot) {
    const unisphereConfig = (0, devkit_1.readJson)(tree, '.unisphere');
    const oldPackageConfig = unisphereConfig.elements.packages[packageIdentifier];
    unisphereConfig.elements.packages[packageIdentifier] = {
        ...oldPackageConfig,
        sourceRoot: newSourceRoot,
    };
    (0, devkit_1.writeJson)(tree, '.unisphere', unisphereConfig);
    devkit_1.logger.info(`✅ Updated .unisphere configuration`);
}
/**
 * Update package.json name to ensure it matches Unisphere naming conventions
 * Nx move generator sets the package name, but we verify it matches our conventions
 */
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);
    // Only update if Nx didn't set it to our expected name
    if (packageJson.name !== newPackageJsonName) {
        packageJson.name = newPackageJsonName;
        (0, devkit_1.writeJson)(tree, packageJsonPath, packageJson);
        devkit_1.logger.info(`✅ Updated package.json name to "${newPackageJsonName}"`);
    }
}
/**
 * Update package-lock.json to rename the package path keys
 * Nx's move generator doesn't handle package-lock.json updates
 */
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) {
            // Check if this key is exactly the old package path
            if (key === oldPath) {
                newPackages[newPath] = value;
                updated = true;
            }
            else {
                newPackages[key] = value;
            }
        }
        packageLock.packages = newPackages;
    }
    // Update resolved paths if they exist
    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`);
    }
}
/**
 * Update vite.config.ts to rename cache directory paths
 * Nx's move generator doesn't update arbitrary string literals in code files
 */
function updateViteConfig(tree, newPath, packageIdentifier) {
    const viteConfigPath = `${newPath}/vite.config.ts`;
    if (!tree.exists(viteConfigPath)) {
        return;
    }
    let content = tree.read(viteConfigPath, 'utf-8');
    if (!content) {
        return;
    }
    const pattern = `package-${packageIdentifier}`;
    // The cache directory already uses the package identifier, so we just verify it's there
    // No changes needed for scope change
    if (content.includes(pattern)) {
        devkit_1.logger.info(`✅ Verified vite.config.ts cache directory`);
    }
}
/**
 * Update project.json to fix lintFilePatterns and comments
 * Nx's move generator updates root/sourceRoot but not arbitrary strings in options
 */
function updateProjectJson(tree, newPath, oldPath, nxProjectName) {
    const projectJsonPath = `${newPath}/project.json`;
    if (!tree.exists(projectJsonPath)) {
        return;
    }
    let content = tree.read(projectJsonPath, 'utf-8');
    if (!content) {
        return;
    }
    let updated = false;
    // Update lintFilePatterns that still reference the old path
    if (content.includes(oldPath)) {
        content = content.replace(new RegExp(escapeRegExp(oldPath), 'g'), newPath);
        updated = true;
    }
    if (updated) {
        tree.write(projectJsonPath, content);
        devkit_1.logger.info(`✅ Updated project.json paths and comments`);
    }
}
/**
 * Update README.md to reflect the new package name
 * Nx's move generator doesn't update documentation files
 */
function updateReadme(tree, newPath, oldPackageJsonName, newPackageJsonName) {
    const readmePath = `${newPath}/README.md`;
    if (!tree.exists(readmePath)) {
        return;
    }
    let content = tree.read(readmePath, 'utf-8');
    if (!content) {
        return;
    }
    let updated = false;
    // Replace old package.json name with new package.json name
    if (content.includes(oldPackageJsonName)) {
        content = content.replace(new RegExp(escapeRegExp(oldPackageJsonName), 'g'), newPackageJsonName);
        updated = true;
    }
    if (updated) {
        tree.write(readmePath, content);
        devkit_1.logger.info(`✅ Updated README.md`);
    }
}
/**
 * Escape special regex characters in a string
 */
function escapeRegExp(string) {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
async function changePackageScopeGenerator(tree, options) {
    // Validate options
    validateOptions(options);
    devkit_1.logger.info('');
    devkit_1.logger.info('🔄 Starting package scope change...');
    devkit_1.logger.info('');
    // Validate .unisphere exists and get experience name
    const unisphereConfig = (0, utils_1.validateUnisphereConfig)(tree);
    const experienceName = unisphereConfig.name;
    // Convert scope from schema format (without @) to internal format (with @)
    // Schema provides: "kaltura-sdk", we need: "@kaltura-sdk"
    const newScope = `@${options.newScope}`;
    // Find package in .unisphere and validate it exists
    const packageInfo = findPackageInUnisphere(tree, options.packageJsonName, experienceName);
    devkit_1.logger.info(`📦 Package: ${packageInfo.packageIdentifier}`);
    devkit_1.logger.info(`🏷️  Current: ${packageInfo.packageJsonName} (${packageInfo.subdirectory || 'root'}/)`);
    // Validate target scope is different from current
    validateDifferentScope(packageInfo.currentScope, newScope);
    // Calculate new subdirectory and paths
    const newSubdirectory = (0, utils_2.scopeToSubdirectory)(newScope);
    // Validate no collision in target subdirectory
    validateNoCollision(tree, packageInfo.packageIdentifier, newSubdirectory);
    // Validate special package restrictions
    validateSpecialPackageRestrictions(packageInfo.packageIdentifier, newScope);
    // Calculate new package.json name
    const newPackageJsonName = (0, utils_2.calculateNewPackageJsonName)(packageInfo.packageIdentifier, experienceName, newScope);
    // Calculate new paths
    const newPath = `unisphere/packages/${newSubdirectory}/${packageInfo.packageIdentifier}`;
    // Read the actual Nx project name from project.json
    const projectJsonPath = `${packageInfo.sourceRoot}/project.json`;
    if (!tree.exists(projectJsonPath)) {
        throw new Error(`project.json not found at ${projectJsonPath}.\n` +
            'Cannot determine the Nx project name for the move operation.');
    }
    const projectJson = (0, devkit_1.readJson)(tree, projectJsonPath);
    const nxProjectName = projectJson.name;
    if (!nxProjectName) {
        throw new Error(`project.json at ${projectJsonPath} does not have a "name" field.\n` +
            'Cannot determine the Nx project name for the move operation.');
    }
    devkit_1.logger.info(`🏷️  Target: ${newPackageJsonName} (${newSubdirectory}/)`);
    devkit_1.logger.info(`📁 Moving: ${packageInfo.sourceRoot} → ${newPath}`);
    devkit_1.logger.info('');
    // Run Nx's built-in move generator
    devkit_1.logger.info('🔧 Running Nx move generator...');
    try {
        await (0, generators_1.moveGenerator)(tree, {
            projectName: nxProjectName,
            destination: newPath,
            newProjectName: nxProjectName,
            importPath: newPackageJsonName,
            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...');
    // Update .unisphere configuration
    updateUnisphereConfiguration(tree, packageInfo.packageIdentifier, newPath);
    // Update package.json to ensure correct naming
    updatePackageJson(tree, newPath, newPackageJsonName);
    // Update package-lock.json
    updatePackageLock(tree, packageInfo.sourceRoot, newPath);
    // Update vite.config.ts
    updateViteConfig(tree, newPath, packageInfo.packageIdentifier);
    // Update project.json
    updateProjectJson(tree, newPath, packageInfo.sourceRoot, nxProjectName);
    // Update README.md
    updateReadme(tree, newPath, packageInfo.packageJsonName, newPackageJsonName);
    devkit_1.logger.info('');
    devkit_1.logger.info('✅ Package scope changed successfully!');
    devkit_1.logger.info('');
    devkit_1.logger.info('📋 Summary:');
    devkit_1.logger.info(`  • Package: ${packageInfo.packageIdentifier}`);
    devkit_1.logger.info(`  • Old scope: ${packageInfo.currentScope || 'none'}`);
    devkit_1.logger.info(`  • New scope: ${newScope}`);
    devkit_1.logger.info(`  • Old location: ${packageInfo.sourceRoot}`);
    devkit_1.logger.info(`  • New location: ${newPath}`);
    devkit_1.logger.info(`  • Old package.json name: ${packageInfo.packageJsonName}`);
    devkit_1.logger.info(`  • New package.json name: ${newPackageJsonName}`);
    devkit_1.logger.info('');
    devkit_1.logger.info('⚠️  IMPORTANT - Consumers must update:');
    devkit_1.logger.info('  • Update all import statements from old to new package name');
    devkit_1.logger.info('  • Update package.json dependencies if they reference this package');
    devkit_1.logger.info('  • Run npm install to update lock file');
    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 package scope..."');
    devkit_1.logger.info('');
}
exports.default = changePackageScopeGenerator;
