"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CreateService = void 0;
const node_path_1 = require("node:path");
const logger_1 = require("../utils/logger");
const error_handler_1 = require("../utils/error.handler");
const template_service_1 = require("./template.service");
const node_fs_1 = require("node:fs");
const colors_1 = require("../utils/colors");
const clack = __importStar(require("@clack/prompts"));
const components_registry_1 = require("./components/components.registry");
const license_service_1 = __importDefault(require("./license.service"));
/**
 * SetupService handles the interactive project setup flow using @clack/prompts.
 *
 * This service provides a branded, user-friendly setup experience that:
 * - Guides users through project configuration steps
 * - Validates user inputs and provides helpful defaults
 * - Collects feature selections and optional configurations
 * - Generates project structure using the TemplateService
 * - Creates license files when license keys are provided
 *
 * The setup flow follows the design document specification with:
 * 1. Product name input with validation
 * 2. Auto-generated folder name (editable)
 * 3. Multi-select feature customizations
 * 4. Auto-tests yes/no (defaults to Yes)
 * 5. Optional license key (hidden input)
 * 6. Confirmation before creation
 * 7. Progress indication during generation
 */
class CreateService {
    logger = logger_1.Logger.getInstance("create.service");
    templateService = new template_service_1.TemplateService();
    /**
     * Run the interactive setup flow
     */
    async runSetup() {
        try {
            const result = await this.collectSetupData();
            await this.confirmAndCreate(result);
            clack.outro((0, colors_1.green)('✨ Project ready!'));
        }
        catch (error) {
            if (clack.isCancel(error)) {
                clack.cancel('Setup cancelled.');
                process.exit(0);
            }
            throw error;
        }
    }
    /**
     * Run the non-interactive setup flow using provided options
     */
    async runNonInteractiveSetup(options) {
        try {
            // Validate folder name
            if (!/^[a-z0-9-_]+$/i.test(options.folderName.trim())) {
                throw new Error('Folder name can only contain letters, numbers, hyphens, and underscores');
            }
            // Validate license if provided
            if (options.licenseKey) {
                try {
                    const info = license_service_1.default.validateLicense(options.licenseKey);
                    if (info.status !== "Active") {
                        throw new Error(`License is not active: Status=${info.status} Error=${info.errorMessage || 'Unknown issue'}`);
                    }
                }
                catch (error) {
                    throw new Error(`Invalid license: ${error instanceof Error ? error.message : String(error)}`);
                }
            }
            // Validate components
            const availableComponents = components_registry_1.ComponentsRegistry.getComponents();
            const availableComponentNames = availableComponents.map(c => c.name);
            const components = options.components || ['iocd'];
            for (const component of components) {
                if (!availableComponentNames.includes(component)) {
                    throw new Error(`Invalid component: ${component}. Available components: ${availableComponentNames.join(', ')}`);
                }
            }
            // Validate applications
            const availableApps = this.templateService.getAvailableApplicationTemplates();
            const availableAppNames = availableApps.map(t => t.name);
            const applications = options.applications || [];
            for (const app of applications) {
                if (!availableAppNames.includes(app)) {
                    throw new Error(`Invalid application: ${app}. Available applications: ${availableAppNames.join(', ')}`);
                }
            }
            // Determine target directory based on user choice
            const shouldInstallInCurrentDir = options.useCurrentFolder || false;
            const setupData = {
                productName: options.productName,
                productSlug: options.folderName,
                targetDirectory: shouldInstallInCurrentDir ? process.cwd() : (0, node_path_1.resolve)(process.cwd(), options.folderName),
                applications,
                components,
                autoTests: applications.includes('auto-tests'),
                licenseKey: options.licenseKey
            };
            await this.confirmAndCreate(setupData, true, options.source);
            this.logger.info('✨ Project ready!');
        }
        catch (error) {
            throw error;
        }
    }
    /**
     * Collect all setup data through interactive prompts
     */
    async collectSetupData() {
        // Step 1: Product Name
        const productName = await clack.text({
            message: 'What is your product name (can include spaces)?',
            placeholder: 'My Desktop App',
            validate: (value) => {
                if (!value.trim()) {
                    return 'Product name is required';
                }
                if (value.trim().length < 2) {
                    return 'Product name must be at least 2 characters';
                }
                if (!/^[a-z0-9\s\-_.()]+$/i.test(value.trim())) {
                    return 'Product name can only contain letters, numbers, spaces, hyphens, underscores, periods and parentheses.';
                }
                return undefined;
            }
        });
        if (clack.isCancel(productName))
            throw productName;
        // Step 2: Ask whether to create new folder or use current folder
        const createNewFolder = await clack.confirm({
            message: 'Do you want to create a new folder for the project?',
            initialValue: true
        });
        if (clack.isCancel(createNewFolder))
            throw createNewFolder;
        let folderName;
        if (createNewFolder) {
            // Create new folder - generate default name and allow user to modify
            const defaultFolderName = this.generateFolderName(productName);
            const inputFolderName = await clack.text({
                message: 'What folder should we create for your project (no spaces; used for folder, exe, and app bundle)?',
                placeholder: defaultFolderName,
                validate: (value) => {
                    if (!value.trim()) {
                        return 'Folder name is required';
                    }
                    if (!/^[a-z0-9-_]+$/i.test(value.trim())) {
                        return 'Folder name can only contain letters, numbers, hyphens, and underscores';
                    }
                    return undefined;
                }
            });
            if (clack.isCancel(inputFolderName))
                throw inputFolderName;
            folderName = inputFolderName;
        }
        else {
            // Use current directory
            const currentDirName = process.cwd().split('/').pop() || 'my-project';
            folderName = this.generateFolderName(currentDirName);
        }
        // Step 3: Component selection (multi-select
        const selectedComponents = await this.collectComponents();
        // Step 4: Template applications (multi-select)
        const selectedFeatures = await this.collectTemplateApplications();
        // Step 5: Aks for license Key
        let licenseKey;
        const licenseInput = await clack.text({
            message: 'Paste your license key here - press Enter when done:',
            validate: (value) => {
                if (!value.trim()) {
                    return 'License content is required if you have a license';
                }
                try {
                    const info = license_service_1.default.validateLicense(value);
                    if (info.status !== "Active") {
                        return `License is not active: Status=${info.status} Error=${info.errorMessage || 'Unknown issue'}`;
                    }
                }
                catch (error) {
                    return `Invalid license: ${error instanceof Error ? error.message : String(error)}`;
                }
                return undefined;
            }
        });
        if (clack.isCancel(licenseInput))
            throw licenseInput;
        licenseKey = licenseInput;
        return {
            productName,
            productSlug: this.generateFolderName(productName),
            targetDirectory: createNewFolder ? (0, node_path_1.resolve)(process.cwd(), folderName) : process.cwd(),
            applications: selectedFeatures,
            components: selectedComponents,
            autoTests: selectedFeatures.includes('auto-tests'),
            licenseKey: licenseKey || undefined
        };
    }
    async collectComponents() {
        const availableComponents = components_registry_1.ComponentsRegistry.getComponents();
        const selectedComponents = [];
        if (availableComponents.length === 0) {
            this.logger.warn('No components are available for selection.');
            return selectedComponents;
        }
        const componentOptions = availableComponents.map(comp => ({
            value: comp.name,
            label: `${comp.displayName ?? comp.name} [${comp.platforms.join(", ")}] `,
            hint: comp.description || '',
            disabled: comp.required
        }));
        const components = await clack.multiselect({
            message: 'Select any components to include using space, and press enter to complete',
            options: componentOptions,
            initialValues: ["iocd", "demos", "devTools"],
            required: false,
        });
        if (clack.isCancel(components)) {
            throw components;
        }
        selectedComponents.push(...components);
        return selectedComponents;
    }
    /**
     * Collect feature selections using setupUI configuration
     */
    async collectTemplateApplications() {
        const availableTemplateApps = this.templateService.getAvailableApplicationTemplates();
        const selectedTemplateApps = [];
        // include selected by default
        selectedTemplateApps.push(...availableTemplateApps
            .filter(template => template.autoInclude === true)
            .map(t => t.name));
        // Separate features into multi-select and separate questions
        const multiSelectFeatures = availableTemplateApps.filter(template => !template.setupUI?.separate);
        const separateFeatures = availableTemplateApps.filter(template => template.setupUI?.separate);
        // Handle multi-select features
        if (multiSelectFeatures.length > 0) {
            const featureOptions = multiSelectFeatures.map(template => ({
                value: template.name,
                label: template.setupUI?.text || template.description || template.displayName || template.name,
                hint: template.description || ''
            }));
            // Set initial selections based on setupUI.selected
            const initialSelections = multiSelectFeatures
                .filter(template => template.setupUI?.selected === true)
                .map(template => template.name);
            const features = await clack.multiselect({
                message: 'Select any customizations, and press enter',
                options: featureOptions,
                initialValues: initialSelections,
                required: false
            });
            if (clack.isCancel(features))
                throw features;
            selectedTemplateApps.push(...features);
        }
        // Handle separate questions
        for (const template of separateFeatures) {
            const confirmed = await clack.confirm({
                message: template.setupUI?.text || template.description || template.displayName || template.name,
                initialValue: template.setupUI?.selected ?? false
            });
            if (clack.isCancel(confirmed))
                throw confirmed;
            if (confirmed) {
                selectedTemplateApps.push(template.name);
            }
        }
        return selectedTemplateApps;
    }
    /**
     * Show confirmation and create the project
     */
    async confirmAndCreate(setupData, nonInteractive = false, source) {
        if (!nonInteractive) {
            // Step 6: Confirmation
            const confirmed = await clack.confirm({
                message: `Create project "${setupData.productName}" with selected customizations?`,
                initialValue: true
            });
            if (clack.isCancel(confirmed) || !confirmed) {
                clack.cancel('Project creation cancelled.');
                process.exit(0);
            }
        }
        // Step 7: Project Creation with progress
        const s = nonInteractive ? null : clack.spinner();
        if (s) {
            s.start('Creating your project...');
        }
        else {
            this.logger.info('Creating your project...');
        }
        try {
            // Generate project using template service
            const templateOptions = {
                productSlug: setupData.productSlug,
                productName: setupData.productName,
                company: "interop.io",
                copyright: "Copyright © 2024",
                version: "1.0.0",
                targetDirectory: setupData.targetDirectory,
                templateApplications: setupData.applications,
                initialComponents: setupData.components,
                componentSource: source
            };
            await this.templateService.generateProject('ioconnect-desktop', templateOptions);
            // Generate license.json if license key provided
            if (setupData.licenseKey) {
                await this.generateLicenseFile(setupData.targetDirectory, setupData.licenseKey);
            }
            if (s) {
                s.stop('Project created successfully!');
            }
            else {
                this.logger.info('Project created successfully!');
            }
            // Show next steps
            const isCurrentDir = setupData.targetDirectory === process.cwd();
            if (!nonInteractive) {
                const nextSteps = isCurrentDir
                    ? `npm install\nnpm run setup\nnpm run dev`
                    : `cd ${setupData.productSlug}\nnpm install\nnpm run setup\nnpm run dev`;
                clack.note(nextSteps, 'Next steps');
            }
            else {
                const nextSteps = isCurrentDir
                    ? `npm install\n  npm run setup\n  npm run dev`
                    : `cd ${setupData.productSlug}\n  npm install\n  npm run setup\n  npm run dev`;
                this.logger.info(`Next steps:\n  ${nextSteps}`);
            }
        }
        catch (error) {
            if (s) {
                s.stop('Project creation failed');
            }
            else {
                this.logger.error('Project creation failed');
            }
            throw new error_handler_1.CLIError('Failed to create project', {
                code: error_handler_1.ErrorCode.FILE_SYSTEM_ERROR,
                cause: error,
                suggestions: [
                    'Check that the target directory does not already exist',
                    'Ensure you have write permissions in the current directory',
                    'Try running with --verbose for more details'
                ]
            });
        }
    }
    /**
     * Generate NPM-compatible folder name from product name
     */
    generateFolderName(productName) {
        return productName
            .toLowerCase()
            .replace(/[^a-z0-9-]/g, '-')
            .replace(/-+/g, '-')
            .replace(/^-|-$/g, '');
    }
    /**
     * Generate iocd.license.key file with provided license JSON
     */
    async generateLicenseFile(targetDirectory, license) {
        try {
            const licensePath = (0, node_path_1.join)(targetDirectory, 'config', 'iocd.license.key');
            (0, node_fs_1.writeFileSync)(licensePath, license, 'utf-8');
            this.logger.debug(`License file created at: ${licensePath}`);
        }
        catch (error) {
            this.logger.warn('Failed to create license file:', error);
            // Don't fail the entire setup for license generation issues
        }
    }
}
exports.CreateService = CreateService;
//# sourceMappingURL=create.service.js.map