/**
 * Installation Validator
 *
 * Validates that instance factory npm packages are installed locally
 * and that the factory files exist at the specified locations.
 *
 * @module registry/validators/installation-validator
 * @version 2.0.0
 */
import { promises as fs } from 'fs';
import { join } from 'path';
import { createIssue, withFix, withSuggestions, ErrorCodes } from '../types/index.js';
/**
 * Installation Validator
 *
 * Validates that required npm packages are installed locally.
 */
export class InstallationValidator {
    /**
     * Validate installation
     *
     * @param manifest - Manifest to validate
     * @param registryFactories - Factory metadata from registry
     * @param options - Validation options
     * @returns Validation result
     */
    async validate(manifest, registryFactories, options = {}) {
        const errors = [];
        const warnings = [];
        const info = [];
        const projectRoot = options.projectRoot || process.cwd();
        const filePath = options.filePath || 'manifests/implementation.yaml';
        // Check each instance factory
        for (const instanceFactory of manifest.instanceFactories) {
            const registryFactory = registryFactories.get(instanceFactory.name);
            if (!registryFactory) {
                // Factory not in registry - skip (already reported by manifest validator)
                continue;
            }
            // Only check npm packages
            if (registryFactory.source.type !== 'npm') {
                info.push(createIssue('REALIZE-004', 'info', 'installation', 'Non-npm source', `Skipping installation check for "${instanceFactory.name}" (source type: ${registryFactory.source.type})`, filePath));
                continue;
            }
            const packageName = registryFactory.source.package;
            const requiredVersion = registryFactory.source.version;
            // Check if package is installed
            const packageInfo = await this.checkPackageInstallation(packageName, projectRoot);
            if (!packageInfo.installed) {
                const installCmd = `npm install ${packageName}${requiredVersion ? '@' + requiredVersion : ''}`;
                errors.push(withFix(withSuggestions(createIssue(ErrorCodes.PACKAGE_NOT_INSTALLED, 'error', 'installation', 'Package not installed', `The npm package "${packageName}" is required by "${instanceFactory.name}" but is not installed.`, filePath), [
                    `Run: ${installCmd}`,
                    'Or install all dependencies: npm install'
                ]), installCmd));
                continue;
            }
            // Package is installed - check version
            if (requiredVersion && packageInfo.version) {
                if (packageInfo.version !== requiredVersion) {
                    warnings.push(withFix(createIssue(ErrorCodes.VERSION_MISMATCH, 'warning', 'installation', 'Version mismatch', `Package "${packageName}" version mismatch: installed=${packageInfo.version}, required=${requiredVersion}`, filePath), `npm install ${packageName}@${requiredVersion}`));
                }
            }
            // Check if factory file exists
            const entrypoint = registryFactory.source.entrypoint || 'factory.yaml';
            const factoryPath = join(projectRoot, 'node_modules', packageName, entrypoint);
            const factoryExists = await this.checkFileExists(factoryPath);
            if (!factoryExists) {
                errors.push(withSuggestions(createIssue(ErrorCodes.FACTORY_FILE_NOT_FOUND, 'error', 'installation', 'Factory file not found', `The factory file "${entrypoint}" was not found in package "${packageName}" at expected location: ${factoryPath}`, filePath), [
                    'The package may be corrupted',
                    `Try: npm install ${packageName}@${requiredVersion || 'latest'} --force`,
                    'Check that the package contains the factory file'
                ]));
            }
        }
        return {
            valid: errors.length === 0,
            errors,
            warnings,
            info
        };
    }
    /**
     * Check if a package is installed
     *
     * @param packageName - Package name
     * @param projectRoot - Project root directory
     * @returns Package info
     */
    async checkPackageInstallation(packageName, projectRoot) {
        const packageJsonPath = join(projectRoot, 'node_modules', packageName, 'package.json');
        try {
            const packageJsonContent = await fs.readFile(packageJsonPath, 'utf8');
            const packageJson = JSON.parse(packageJsonContent);
            return {
                name: packageName,
                installed: true,
                version: packageJson.version,
                packageJsonPath
            };
        }
        catch (error) {
            if (error.code === 'ENOENT') {
                // Package not installed
                return {
                    name: packageName,
                    installed: false
                };
            }
            // Other error (permission, invalid JSON, etc.) - treat as not installed
            return {
                name: packageName,
                installed: false
            };
        }
    }
    /**
     * Check if a file exists
     *
     * @param filePath - File path to check
     * @returns True if file exists
     */
    async checkFileExists(filePath) {
        try {
            await fs.access(filePath);
            return true;
        }
        catch {
            return false;
        }
    }
    /**
     * Get package info for a specific package
     *
     * @param packageName - Package name
     * @param projectRoot - Project root directory
     * @returns Package info
     */
    async getPackageInfo(packageName, projectRoot) {
        return this.checkPackageInstallation(packageName, projectRoot || process.cwd());
    }
}
//# sourceMappingURL=installation-validator.js.map