"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComponentPlatformUtils = void 0;
const path_1 = require("path");
/**
 * Platform utility for component stores
 *
 * This utility provides shared functionality for:
 * - Platform detection from filenames
 * - Component filename parsing (both OS-specific and OS-agnostic)
 * - Current platform detection
 * - Component matching logic
 * - Version parsing with build timestamp support
 *
 * Supports component naming patterns:
 * 1. OS-specific: <name>-<version>-<arch>-<platform>.ext (e.g., iocd-v10.0.0-build.123-darwin-arm64.dmg)
 * 2. OS-agnostic: <name>-<version>.ext (e.g., webapp-v10.0.0-build.123.zip)
 *
 * Version formats supported:
 * - v<X.Y.Z> (e.g., v10.0.0)
 * - v<X.Y.Z>-build.<TIMESTAMP> (e.g., v10.0.0-build.20250927121929)
 * - v<X.Y.Z.B> (e.g., v10.0.0.1)
 */
class ComponentPlatformUtils {
    /**
     * Get current platform string compatible with component naming
     */
    static getCurrentPlatform() {
        const platform = process.platform;
        if (platform === "win32") {
            return "win32";
        }
        if (platform === "darwin") {
            // Check if ARM64 based on process.arch
            return "darwin";
        }
        // Fallback to win32 if unknown to support building on linux
        return "win32";
    }
    /**
     * Parse component information from filename
     *
     * Supports component patterns:
     * - OS-specific: <component>-<version>-<arch>-<platform>.<ext>
     * - OS-agnostic: <component>-<version>.<ext>
     *
     * @param filename The component filename to parse
     * @returns Parsed component info
     * @throws Error if parsing fails
     */
    static parseComponentFilename(filename) {
        const nameWithoutExt = (0, path_1.parse)(filename).name;
        return this.parseComponentName(nameWithoutExt);
    }
    /**
     * Parse component information from a name (not filename)
     *
     * Supports component patterns:
     * - OS-specific with platform-arch: <component>-<version>-<platform>-<arch>
     * - OS-specific with platform only: <component>-<version>-<platform>
     * - OS-agnostic: <component>-<version>
     *
     * @param name The component name to parse
     * @returns Parsed component info
     * @throws Error if parsing fails
     */
    static parseComponentName(name) {
        // Pattern 1: OS-specific format with platform-arch order: <component>-<version>[-<platform>-<arch> OR .<platform>-<arch>]
        // Examples:
        // - iocd-v10.0.1-build.20250827150311-win32-x64 (dash separated)
        // - iocd-v10.0.0-build.20250927121929.darwin-arm64 (dot separated after build)
        // - iocd-v10.0.3-build.251205T1046-darwin-arm64 (timestamp with T format)
        // Must have EXACTLY 4 parts: component, version, platform, arch
        const osSpecificPlatformArchPattern = /^(.+)-(v\d+\.\d+\.\d+(?:\.\d+)?(?:-build\.[\dT]+)?)[.-]([^-]+)-([^-.]+)$/;
        const osSpecificPlatformArchMatch = name.match(osSpecificPlatformArchPattern);
        if (osSpecificPlatformArchMatch) {
            const [, componentName, version, platformPart, arch] = osSpecificPlatformArchMatch;
            // Validate that this is actually a 4-part pattern by checking if arch looks like an architecture
            const validArchs = ["x64", "x86", "arm64", "ia32", "arm"];
            if (!validArchs.includes(arch.toLowerCase())) {
                // This might be a platform-only pattern that got matched incorrectly, skip to next pattern
                // Fall through to Pattern 2
            }
            else {
                const versionInfo = this.parseVersionString(version);
                // Map platform names that might not match our enum exactly
                let platform;
                if (platformPart === "win32" || platformPart.startsWith("win")) {
                    platform = "win32";
                }
                else if (platformPart === "darwin" ||
                    platformPart.startsWith("mac") ||
                    platformPart.startsWith("osx")) {
                    platform = "darwin";
                }
                else {
                    platform = platformPart;
                }
                const result = {
                    name: componentName,
                    version: version,
                    baseVersion: versionInfo.baseVersion,
                    isOsSpecific: true,
                    platform: platform,
                    arch: arch,
                };
                if (versionInfo.buildInfo) {
                    result.buildInfo = versionInfo.buildInfo;
                }
                if (versionInfo.buildNumber) {
                    result.buildNumber = versionInfo.buildNumber;
                }
                return result;
            }
        }
        // Pattern 2: OS-specific format with platform only <component>-<version>[-<platform> OR .<platform>]
        // Examples:
        // - bbgv2-v2.0.0.1-win32 (dash separated)
        // - myapp-v1.0.0-build.123-darwin (dash separated with build)
        // - myapp-v1.0.0-build.123.darwin (dot separated after build)
        // - myapp-v1.0.0-build.251205T1046-darwin (timestamp with T format)
        const osSpecificPlatformOnlyPattern = /^(.+)-(v\d+\.\d+\.\d+(?:\.\d+)?(?:-build\.[\dT]+)?)[.-](darwin|win32)$/;
        const osSpecificPlatformOnlyMatch = name.match(osSpecificPlatformOnlyPattern);
        if (osSpecificPlatformOnlyMatch) {
            const [, componentName, version, platform] = osSpecificPlatformOnlyMatch;
            const versionInfo = this.parseVersionString(version);
            const result = {
                name: componentName,
                version: version,
                baseVersion: versionInfo.baseVersion,
                isOsSpecific: true,
                platform: platform,
            };
            if (versionInfo.buildInfo) {
                result.buildInfo = versionInfo.buildInfo;
            }
            if (versionInfo.buildNumber) {
                result.buildNumber = versionInfo.buildNumber;
            }
            return result;
        }
        // Pattern 3: OS-agnostic format <component>-<version>
        // e.g., webapp-v10.0.0-build.123, webapp-v10.0.0, webapp-v10.0.3-build.251205T1046
        const osAgnosticPattern = /^(.+)-(v\d+\.\d+\.\d+(?:\.\d+)?(?:-build\.[\dT]+)?)$/;
        const osAgnosticMatch = name.match(osAgnosticPattern);
        if (osAgnosticMatch) {
            const [, componentName, version] = osAgnosticMatch;
            const versionInfo = this.parseVersionString(version);
            const result = {
                name: componentName,
                version: version,
                baseVersion: versionInfo.baseVersion,
                isOsSpecific: false,
            };
            if (versionInfo.buildInfo) {
                result.buildInfo = versionInfo.buildInfo;
            }
            if (versionInfo.buildNumber) {
                result.buildNumber = versionInfo.buildNumber;
            }
            return result;
        }
        throw new Error(`${name} does not match expected component patterns.`);
    }
    /**
     * Check if a filename matches the current platform
     *
     * @param filename The filename to check
     * @param currentPlatform Optional current platform (defaults to auto-detect)
     * @returns True if the component matches the current platform
     */
    static matchesCurrentPlatform(filename, currentPlatform) {
        const platform = currentPlatform || this.getCurrentPlatform();
        const parsed = this.parseComponentFilename(filename);
        // OS-agnostic components match any platform
        if (!parsed.isOsSpecific) {
            return true;
        }
        if (!parsed.platform) {
            throw new Error(`Component is not OS-specific but platform is missing in filename: ${filename}`);
        }
        const platformString = parsed.platform.toLowerCase();
        let detectedPlatform = null;
        if (platformString.includes("darwin") || platformString.includes("macos")) {
            detectedPlatform = "darwin";
        }
        if (platformString.includes("win32") ||
            platformString.includes("windows")) {
            detectedPlatform = "win32";
        }
        if (!detectedPlatform) {
            throw new Error(`Unable to determine platform from component filename: ${filename}`);
        }
        return detectedPlatform === platform;
    }
    /**
     * Check if a file is a supported package format
     */
    static isSupportedPackageFile(filename) {
        const supportedExtensions = [".zip", ".dmg", ".exe"];
        const lowerFilename = filename.toLowerCase();
        return supportedExtensions.some(ext => lowerFilename.endsWith(ext));
    }
    /**
     * Parse version string to extract base version and build information
     *
     * @param version Version string (e.g., "v10.0.0-build.20250927121929")
     * @returns Parsed version info
     */
    static parseVersionString(version) {
        // Remove 'v' prefix if present
        const cleanVersion = version.startsWith("v")
            ? version.substring(1)
            : version;
        // Check for build suffix (supports both numeric and timestamp formats)
        // e.g., build.20250927121929 or build.251205T1046
        const buildMatch = cleanVersion.match(/^(\d+\.\d+\.\d+(?:\.\d+)?)-build\.([\dT]+)$/);
        if (buildMatch) {
            return {
                baseVersion: buildMatch[1],
                buildInfo: `build.${buildMatch[2]}`,
                buildNumber: Number(buildMatch[2].replace('T', '')),
                timestamp: buildMatch[2],
            };
        }
        // No build suffix
        return {
            baseVersion: cleanVersion,
        };
    }
    /**
     * Parse platform part to extract platform and architecture
     *
     * @param platformPart Platform string (e.g., "darwin-arm64", "win32", "darwin")
     * @returns Parsed platform info
     */
    static parsePlatformPart(platformPart) {
        const parts = platformPart.toLowerCase().split("-");
        if (parts.length === 1) {
            // Simple platform (e.g., "darwin", "win32")
            const platform = parts[0];
            if (platform === "darwin" || platform === "win32") {
                return { platform: platform };
            }
        }
        else if (parts.length === 2) {
            // Platform with arch (e.g., "darwin-arm64", "win32-x64")
            const [platform, arch] = parts;
            if (platform === "darwin" || platform === "win32") {
                return {
                    platform: platform,
                    arch: arch,
                };
            }
        }
        throw new Error(`Unable to parse platform part: ${platformPart}`);
    }
    /**
     * Normalize version string by removing build suffix
     *
     * @param version Version string
     * @returns Base version without build info
     */
    static normalizeVersion(version) {
        return this.parseVersionString(version).baseVersion;
    }
    /**
     * Compare two version strings with build timestamp support
     * Priority: non-build > newer build > older build
     *
     * @param a First version string
     * @param b Second version string
     * @returns -1 if a < b, 1 if a > b, 0 if equal
     */
    static compareVersionsWithBuild(a, b) {
        if (a === "latest" && b === "latest")
            return 0;
        if (a === "latest")
            return 1;
        if (b === "latest")
            return -1;
        const aInfo = this.parseVersionString(a);
        const bInfo = this.parseVersionString(b);
        // First compare base versions
        const baseComparison = this.compareSemanticVersions(aInfo.baseVersion, bInfo.baseVersion);
        if (baseComparison !== 0) {
            return baseComparison;
        }
        // Base versions are equal, now consider build info
        // Non-build versions are preferred over build versions
        if (!aInfo.buildInfo && !bInfo.buildInfo)
            return 0;
        if (!aInfo.buildInfo && bInfo.buildInfo)
            return 1; // a preferred (no build)
        if (aInfo.buildInfo && !bInfo.buildInfo)
            return -1; // b preferred (no build)
        // Both have build info, compare timestamps (higher is newer)
        if (aInfo.timestamp && bInfo.timestamp) {
            const aTimestamp = parseInt(aInfo.timestamp);
            const bTimestamp = parseInt(bInfo.timestamp);
            return aTimestamp > bTimestamp ? 1 : aTimestamp < bTimestamp ? -1 : 0;
        }
        return 0;
    }
    /**
     * Compare semantic version strings (X.Y.Z or X.Y.Z.B)
     *
     * @param a First version
     * @param b Second version
     * @returns -1 if a < b, 1 if a > b, 0 if equal
     */
    static compareSemanticVersions(a, b) {
        const aParts = a.split(".").map(Number);
        const bParts = b.split(".").map(Number);
        const maxLength = Math.max(aParts.length, bParts.length);
        for (let i = 0; i < maxLength; i++) {
            const aPart = aParts[i] || 0;
            const bPart = bParts[i] || 0;
            if (aPart > bPart)
                return 1;
            if (aPart < bPart)
                return -1;
        }
        return 0;
    }
    /**
     * Compare two version strings for sorting (backwards compatibility)
     * For new build-aware comparison, use compareVersionsWithBuild()
     *
     * @param a First version string
     * @param b Second version string
     * @returns -1 if a < b, 1 if a > b, 0 if equal
     */
    static compareVersions(a, b) {
        if (a === "latest" && b === "latest")
            return 0;
        if (a === "latest")
            return 1;
        if (b === "latest")
            return -1;
        // Simple string comparison - for production use, consider a proper semver library
        if (a < b)
            return -1;
        if (a > b)
            return 1;
        return 0;
    }
}
exports.ComponentPlatformUtils = ComponentPlatformUtils;
//# sourceMappingURL=platform.utils.js.map