"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LocalComponentsStore = void 0;
const logger_1 = require("../../../utils/logger");
const fs_1 = require("fs");
const path_1 = require("path");
const error_handler_1 = require("../../../utils/error.handler");
const platform_utils_1 = require("../platform.utils");
/**
 * Local Components Store implementation
 *
 * This store reads components from a local directory following the same structure as the S3 store.
 * Expected directory structure:
 *
 * componentsDirectory/
 * ├── component1/
 * │   ├── v1.0.0/
 * │   │   ├── component1-v1.0.0-win32.zip
 * │   │   ├── component1-v1.0.0-darwin.dmg
 * │   │   └── component1-v1.0.0-darwin-arm64.dmg
 * │   └── v1.1.0/
 * │       ├── component1-v1.1.0-win32.zip
 * │       └── component1-v1.1.0-darwin.dmg
 * └── component2/
 *     └── v2.0.0/
 *         └── component2-v2.0.0-win32.zip
 *
 * Legacy support is maintained for flat file structures in the root directory.
 */
class LocalComponentsStore {
    logger = logger_1.Logger.getInstance("local.store");
    componentsDirectory;
    constructor(componentsDirectory) {
        this.componentsDirectory = (0, path_1.resolve)(componentsDirectory);
        this.validateDirectory();
    }
    getInfo() {
        return `Local Components Store (${this.componentsDirectory})`;
    }
    async getAll() {
        this.logger.debug(`Scanning local components directory: ${this.componentsDirectory}`);
        if (!(0, fs_1.existsSync)(this.componentsDirectory)) {
            this.logger.warn(`Components directory does not exist: ${this.componentsDirectory}`);
            return [];
        }
        const components = [];
        try {
            const componentDirs = (0, fs_1.readdirSync)(this.componentsDirectory);
            for (const componentName of componentDirs) {
                const componentPath = (0, path_1.join)(this.componentsDirectory, componentName);
                // Skip hidden directories and files
                if (componentName.startsWith(".")) {
                    continue;
                }
                try {
                    const stat = (0, fs_1.statSync)(componentPath);
                    if (stat.isDirectory()) {
                        // Scan for version directories inside component directory
                        const versionComponents = await this.scanComponentVersions(componentPath);
                        components.push(...versionComponents);
                    }
                    else if (stat.isFile() &&
                        this.isSupportedPackageFile(componentName)) {
                        // Legacy support: Handle single package files in root directory
                        this.logger.warn(`Found legacy component file in root: ${componentName}. Consider moving to component/version structure.`);
                        const component = await this.createComponentFromFile(componentPath, componentName);
                        if (component) {
                            components.push(component);
                        }
                    }
                }
                catch (error) {
                    this.logger.warn(`Failed to process component ${componentName}: ${error}`);
                    continue;
                }
            }
            this.logger.debug(`Found ${components.length} components in local store`);
            return components;
        }
        catch (error) {
            this.logger.error(`Failed to scan components directory: ${error}`);
            throw new Error(`Failed to read local components: ${error}`);
        }
    }
    async download(name, version) {
        this.logger.debug(`Downloading component ${name}@${version} from local store`);
        const componentPath = (0, path_1.join)(this.componentsDirectory, name);
        if (!(0, fs_1.existsSync)(componentPath)) {
            throw new error_handler_1.CLIError(`Component ${name} not found in local store at path: ${componentPath}`);
        }
        const components = await this.scanComponentVersions(componentPath);
        // filter out based on the current platform
        const currentPlatform = platform_utils_1.ComponentPlatformUtils.getCurrentPlatform();
        const platformMatches = components.filter(c => c.platform === currentPlatform);
        if (platformMatches.length === 0) {
            throw new error_handler_1.CLIError(`No versions of component ${name} found for current platform (${platform_utils_1.ComponentPlatformUtils.getCurrentPlatform()}) in local store.`, {
                suggestions: [
                    `Check that the component ${name} has versions built for your platform (${platform_utils_1.ComponentPlatformUtils.getCurrentPlatform()})`,
                    `Available versions: ${components.map(c => `${c.version} (${c.platform})`).join(", ") || "none"}`,
                    `Ensure your local store follows the structure: ${this.componentsDirectory}/${name}/v${version}/${name}-v${version}-${platform_utils_1.ComponentPlatformUtils.getCurrentPlatform()}.ext`,
                ],
            });
        }
        // Find matching component, preferring current platform
        let matchingComponent;
        if (version === "latest") {
            matchingComponent = this.getLatestVersion(platformMatches);
        }
        else {
            // Look for exact version match, preferring current platform
            matchingComponent = platformMatches.find(c => c.name === name && c.version === version);
        }
        if (!matchingComponent) {
            const availableVersions = components
                .filter(c => c.name === name)
                .map(c => `${c.version} (${c.platform})`)
                .join(", ");
            throw new error_handler_1.CLIError(`Component ${name}${version ? `@${version}` : ""} not found in local store. `, {
                suggestions: [
                    `You are trying to install ${name}@${version} - check that the component name and version are correct`,
                    `Available versions: ${availableVersions || "none"}`,
                    `Ensure your local store follows the structure: ${this.componentsDirectory}/${name}/v${version}/${name}-v${version}-${currentPlatform}.ext`,
                ],
            });
        }
        // Use downloadUrl as the file path for local components
        const filePath = matchingComponent.downloadUrl;
        if (!filePath || !(0, fs_1.existsSync)(filePath)) {
            throw new Error(`Component file not found: ${filePath}`);
        }
        try {
            // Read the file as binary data
            const data = (0, fs_1.readFileSync)(filePath);
            this.logger.debug(`Downloaded ${name}@${matchingComponent.version} (${matchingComponent.platform}) from local store`);
            return {
                name: matchingComponent.name,
                data: data,
                filename: (0, path_1.basename)(filePath),
                actualVersion: matchingComponent.version,
            };
        }
        catch (error) {
            this.logger.error(`Failed to read component file ${filePath}: ${error}`);
            throw new Error(`Failed to download component: ${error}`);
        }
    }
    /**
     * Validate that the components directory exists and is accessible
     */
    validateDirectory() {
        if (!(0, fs_1.existsSync)(this.componentsDirectory)) {
            throw new Error(`Components directory does not exist: ${this.componentsDirectory}`);
        }
        try {
            const stat = (0, fs_1.statSync)(this.componentsDirectory);
            if (!stat.isDirectory()) {
                throw new Error(`Path is not a directory: ${this.componentsDirectory}`);
            }
        }
        catch (error) {
            throw new Error(`Cannot access components directory: ${this.componentsDirectory} (${error})`);
        }
    }
    /**
     * Check if a file is a supported package format
     */
    isSupportedPackageFile(filename) {
        return platform_utils_1.ComponentPlatformUtils.isSupportedPackageFile(filename);
    }
    /**
     * Create a Component object from a single package file (legacy support)
     */
    async createComponentFromFile(filePath, filename) {
        try {
            const parsed = platform_utils_1.ComponentPlatformUtils.parseComponentFilename(filename);
            if (!parsed) {
                this.logger.debug(`Could not parse component info from filename: ${filename}`);
                return null;
            }
            const component = {
                name: parsed.name,
                version: parsed.version,
                platform: parsed.platform || platform_utils_1.ComponentPlatformUtils.getCurrentPlatform(),
                downloadUrl: filePath, // Store file path as download URL for local components
                filename: filename,
                prerelease: false, // Legacy files are assumed stable
            };
            this.logger.debug(`Created component from file: ${component.name}@${component.version} (${component.platform})`);
            return component;
        }
        catch (error) {
            this.logger.warn(`Failed to create component from file ${filePath}: ${error}`);
            return null;
        }
    }
    /**
     * Create a Component object from structured directory layout
     * Expected filename format: componentName-vVersion-platform.ext
     * Following S3 store structure: componentName/vVersion/componentName-vVersion-platform.ext
     */
    async createStructuredComponent(filepath) {
        try {
            const filename = (0, path_1.basename)(filepath);
            const componentInfo = platform_utils_1.ComponentPlatformUtils.parseComponentFilename(filename);
            const component = {
                name: componentInfo.name,
                version: componentInfo.baseVersion,
                fullVersion: componentInfo.version,
                platform: componentInfo.platform || platform_utils_1.ComponentPlatformUtils.getCurrentPlatform(),
                downloadUrl: filepath,
                filename,
                prerelease: false, // Structured local files are assumed stable
            };
            this.logger.debug(`Created structured component: ${component.name}@${component.version} (${component.platform})`);
            return component;
        }
        catch (error) {
            this.logger.error(`Failed to create structured component from file ${filepath}: ${error}`);
            return null;
        }
    }
    /**
     * Scan component directory for version subdirectories
     * Expected structure: componentName/vX.Y.Z/componentName-vX.Y.Z-platform.ext
     */
    async scanComponentVersions(componentPath) {
        const components = [];
        try {
            const versionDirs = (0, fs_1.readdirSync)(componentPath);
            for (const versionDir of versionDirs) {
                // ignore directories that do not start with v
                if (!versionDir.startsWith("v")) {
                    continue;
                }
                const versionPath = (0, path_1.join)(componentPath, versionDir);
                const stat = (0, fs_1.statSync)(versionPath);
                if (!stat.isDirectory()) {
                    continue;
                }
                // Scan for component files in version directory
                const versionComponents = await this.scanVersionDirectory(versionPath);
                components.push(...versionComponents);
            }
        }
        catch (error) {
            this.logger.warn(`Failed to scan component versions directory ${componentPath}: ${error}`);
        }
        return components;
    }
    /**
     * Scan version directory for component package files
     * Expected structure: vX.Y.Z/componentName-vX.Y.Z-platform.ext
     */
    async scanVersionDirectory(versionPath) {
        const components = [];
        try {
            const files = (0, fs_1.readdirSync)(versionPath);
            for (const filename of files) {
                if (filename.startsWith(".")) {
                    continue;
                }
                const filePath = (0, path_1.join)(versionPath, filename);
                const stat = (0, fs_1.statSync)(filePath);
                if (stat.isFile() && this.isSupportedPackageFile(filename)) {
                    const component = await this.createStructuredComponent(filePath);
                    if (component) {
                        components.push(component);
                    }
                }
            }
        }
        catch (error) {
            this.logger.warn(`Failed to scan version directory ${versionPath}: ${error}`);
        }
        return components;
    }
    /**
     * Get the latest version from a list of components with the same name
     * Simple string comparison - for proper semver, you'd want to use a semver library
     */
    getLatestVersion(components) {
        return components.reduce((latest, current) => {
            // Simple string comparison - "latest" always wins, otherwise lexicographic
            if (current.version === "latest")
                return current;
            if (latest.version === "latest")
                return latest;
            return current.version > latest.version ? current : latest;
        });
    }
}
exports.LocalComponentsStore = LocalComponentsStore;
//# sourceMappingURL=local.store.js.map