"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addApplicationGenerator = addApplicationGenerator;
const tslib_1 = require("tslib");
const devkit_1 = require("@nx/devkit");
const path = tslib_1.__importStar(require("path"));
const utils_1 = require("../utils");
const dependency_config_1 = require("../dependency-config");
function validateOptions(options) {
    if (!options.name || options.name.trim() === '') {
        throw new Error(`Missing required option: 'name'\n` +
            `  Description: The name of the application to create\n` +
            `  Type: string\n` +
            `  Pattern: ^[a-zA-Z][a-zA-Z0-9\\-\\s]*$\n` +
            `  Example: nx g @unisphere/nx:add-application --name=my-app --servingType=self-hosted`);
    }
    const namePattern = /^[a-zA-Z][a-zA-Z0-9\-\s]*$/;
    if (!namePattern.test(options.name)) {
        throw new Error(`Invalid value '${options.name}' for option 'name'\n` +
            `  Pattern: ^[a-zA-Z][a-zA-Z0-9\\-\\s]*$\n` +
            `  Received: ${options.name}`);
    }
    if (!options.servingType) {
        throw new Error(`Missing required option: 'servingType'\n` +
            `  Description: How should this app be served?\n` +
            `  Allowed values: local-dev-playground, self-hosted, iframe-with-post-messages, assets-only\n` +
            `  Example: nx g @unisphere/nx:add-application --name=my-app --servingType=self-hosted`);
    }
    const allowedServingTypes = ['local-dev-playground', 'self-hosted', 'iframe-with-post-messages', 'assets-only'];
    if (!allowedServingTypes.includes(options.servingType)) {
        throw new Error(`Invalid value '${options.servingType}' for option 'servingType'\n` +
            `  Allowed values: local-dev-playground, self-hosted, iframe-with-post-messages, assets-only\n` +
            `  Received: ${options.servingType}`);
    }
    const allowedDependencies = ['ds', 'mui', 'react-hook-form'];
    if (options.dependencies) {
        for (const dep of options.dependencies) {
            if (!allowedDependencies.includes(dep)) {
                throw new Error(`Invalid value '${dep}' in option 'dependencies'\n` +
                    `  Allowed values: ${allowedDependencies.join(', ')}\n` +
                    `  Received: ${dep}`);
            }
        }
    }
}
async function addApplicationGenerator(tree, options) {
    // Validate options
    validateOptions(options);
    // Validate iframe-with-post-messages is not supported
    if (options.servingType === 'iframe-with-post-messages') {
        throw new Error('iframe-with-post-messages serving type is not currently supported. Please choose a different serving type.');
    }
    const isPlayground = options.servingType === 'local-dev-playground';
    const isAssetsOnly = options.servingType === 'assets-only';
    // Validate and read .unisphere configuration
    const unisphereConfig = (0, utils_1.validateUnisphereConfig)(tree);
    let runtimeName = options.runtimeName || '';
    let visualName = options.visualName || '';
    let selectedDependencies = [];
    if (isAssetsOnly) {
        options.htmlPageTitle = options.htmlPageTitle || options.name;
        selectedDependencies = [];
    }
    else {
        if (!isPlayground && !options.htmlPageTitle) {
            options.htmlPageTitle = options.name;
        }
        const baseDependencies = ['react'];
        selectedDependencies = [...baseDependencies, ...(options.dependencies || [])];
    }
    // Validate runtime exists if runtimeName is provided
    if (runtimeName) {
        if (!(0, utils_1.checkIfRuntimeExists)(tree, runtimeName)) {
            throw new Error(`Runtime "${runtimeName}" does not exist, please check the available runtimes under 'unisphere/runtimes'`);
        }
    }
    // Extract environment variables
    const widgetName = unisphereConfig.name;
    let applicationName = options.name;
    if (isPlayground && !options.name.endsWith('-playground')) {
        applicationName += '-playground';
    }
    const applicationNameAsLowerDashCase = (0, devkit_1.names)(applicationName).fileName;
    const typesPackageInfo = (0, utils_1.findTypesOrCorePackageInfo)(tree);
    // Determine subdirectory based on serving type
    // local-dev-playground -> local/, other types -> server/
    const subdirectory = isPlayground ? 'local' : 'server';
    const projectRoot = `unisphere/applications/${subdirectory}/${applicationNameAsLowerDashCase}`;
    const hasDs = selectedDependencies.includes('ds');
    const templateVariables = {
        htmlPageTitle: options.servingType === 'local-dev-playground'
            ? `${runtimeName || 'Runtime'} - Local Dev Application`
            : options.htmlPageTitle,
        servingType: options.servingType,
        hasDs,
        ...(0, utils_1.createNameTransforms)(applicationNameAsLowerDashCase, 'applicationName'),
        ...(0, utils_1.createNameTransforms)(widgetName, 'widgetName'),
        ...(0, utils_1.createNameTransforms)(runtimeName, 'runtimeName'),
        ...(0, utils_1.createNameTransforms)(visualName, 'visualName'),
        tmpl: '',
        typesAlias: typesPackageInfo.alias,
        subdirectory,
        projectRoot,
    };
    // Check if name already exists across all element types in .unisphere
    (0, utils_1.validateElementNameUnique)(tree, applicationNameAsLowerDashCase, 'applications');
    // Check if the application already exists on filesystem
    if (tree.exists(projectRoot)) {
        throw new Error(`Application "${applicationNameAsLowerDashCase}" already exists at ${projectRoot}.\n` +
            'Please choose a different application name or remove the existing application first.');
    }
    // Choose template based on serving type
    const templatePath = isAssetsOnly ? 'templates/assets-only' : 'templates/default';
    // Generate files from templates
    (0, devkit_1.generateFiles)(tree, path.join(__dirname, templatePath), projectRoot, templateVariables);
    // For local-dev-playground apps, generate setup-local-runtimes.ts
    if (isPlayground) {
        const rawConfig = (0, devkit_1.readJson)(tree, '.unisphere');
        const experienceName = `unisphere.${rawConfig.type}.${rawConfig.name}`;
        const setupLocalRuntimesContent = `export function setupLocalRuntimes() {
  if (!process.env.LOCAL_RUNTIMES) return;

  const runtimes: Record<string, { token: string; baseEnvName: string }> = {};
  process.env.LOCAL_RUNTIMES.split(',').forEach((entry) => {
    const [name, port] = entry.split('@');
    if (name && port) {
      runtimes[name] = { token: port, baseEnvName: '' };
    }
  });

  const overrides = {
    baseEnvName: '',
    mode: 'integration',
    options: { verbose: 'inherit' },
    baseEnvScope: { core: 'inherit', presets: 'inherit' },
    runtimes: {
      '${experienceName}': runtimes,
    },
  };

  (window as any).unisphereRuntimeOverrides = btoa(JSON.stringify(overrides));
}
`;
        tree.write(path.join(projectRoot, 'src', 'setup-local-runtimes.ts'), setupLocalRuntimesContent);
    }
    // Install selected dependencies
    (0, dependency_config_1.installSelectedDependencies)(tree, selectedDependencies);
    // Update .unisphere configuration
    (0, utils_1.updateUnisphereConfig)(tree, 'applications', applicationNameAsLowerDashCase, {
        sourceRoot: projectRoot,
    });
    // Update tsconfig.base.json with path mapping
    const applicationPathKey = `unisphere-application-${applicationNameAsLowerDashCase}`;
    const applicationPathValue = `${projectRoot}/src/index.ts`;
    (0, utils_1.updateTsConfigPaths)(tree, applicationPathKey, applicationPathValue);
    const scriptName = `serve:${applicationNameAsLowerDashCase}`;
    if (!isAssetsOnly) {
        const scriptCommand = runtimeName ?
            `concurrently "npx unisphere runtime serve ${runtimeName} --port 8400" "npx unisphere application serve ${applicationNameAsLowerDashCase} --port 4002"`
            : `npx unisphere application serve ${applicationNameAsLowerDashCase} --port 4002`;
        (0, utils_1.addScriptToRootPackageJson)(tree, scriptName, scriptCommand);
    }
    await (0, devkit_1.formatFiles)(tree);
    // Return a function that will be executed after all file operations are complete
    const hasExtraDependencies = selectedDependencies.length > 0;
    return () => {
        if (hasExtraDependencies) {
            (0, devkit_1.installPackagesTask)(tree, true);
        }
        devkit_1.logger.info('');
        devkit_1.logger.info('✅ Application generated successfully!');
        devkit_1.logger.info('');
        devkit_1.logger.info(`🚀 Application Name: ${applicationNameAsLowerDashCase}`);
        devkit_1.logger.info(`🔧 Serving Type: ${options.servingType}`);
        devkit_1.logger.info(`📁 Location: ${projectRoot}`);
        devkit_1.logger.info('');
        if (isAssetsOnly) {
            devkit_1.logger.info(`📂 Place your assets in: ${projectRoot}/src/assets/`);
        }
        else {
            devkit_1.logger.info(`📜 To run the application locally: npm run ${scriptName}`);
        }
        devkit_1.logger.info('');
    };
}
exports.default = addApplicationGenerator;
