"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitHubComponentsStore = void 0;
const error_handler_1 = require("../../../utils/error.handler");
const logger_1 = require("../../../utils/logger");
const platform_utils_1 = require("../platform.utils");
const version_selector_1 = require("../version.selector");
;
// The github release package name is in format {{component}}-{{version}}
// each release has multiple assets; each asset can be platform specific
// each release has a tag the same as the release name; this can be used to get a specific release
class GitHubComponentsStore {
    baseUrl = '';
    usePrereleases = false;
    logger = logger_1.Logger.getInstance();
    cachedReleases = null;
    constructor(repo = "interopio/desktop-releases/releases", usePrereleases = false) {
        this.baseUrl = `https://api.github.com/repos/${repo}`;
        this.usePrereleases = usePrereleases;
    }
    async getAll() {
        const allReleases = await this.getAllReleases();
        return this.parseReleases(allReleases);
    }
    async download(name, version) {
        // Find the specific release and asset for this component
        const targetComponent = await this.findComponent(name, version);
        if (!targetComponent) {
            throw new Error(`No component found for ${name}@${version}`);
        }
        // Download the asset
        this.logger.debug(`Downloading ${targetComponent.name} from ${targetComponent.downloadUrl}`);
        const response = await fetch(targetComponent.downloadUrl);
        if (!response.ok) {
            throw new Error(`Failed to download asset: ${response.status} ${response.statusText}`);
        }
        // Get the data as buffer
        const buffer = await response.arrayBuffer();
        this.logger.debug(`Downloaded ${name}@${version || 'latest'} (${targetComponent.name}) successfully!`);
        return {
            name: name,
            data: new Uint8Array(buffer),
            filename: targetComponent.filename,
            actualVersion: targetComponent.version
        };
    }
    getInfo() {
        const prereleaseStatus = this.usePrereleases ? "including pre-releases" : "stable releases only";
        return `GitHub Releases Store: ${this.baseUrl} (${prereleaseStatus})`;
    }
    /**
     * Find a specific release by component name and version
     */
    async findComponent(componentName, version) {
        // If no version specified, find the latest release for this component
        const allReleases = await this.getAllReleases();
        // filter out by component name
        let componentReleases = allReleases.filter(release => release.tag_name.startsWith(`${componentName}-v`));
        if (componentReleases.length === 0) {
            const prereleaseNote = this.usePrereleases ? "" : " (pre-releases excluded - set usePrereleases: true to include them)";
            throw new Error(`No releases found for component '${componentName}'${prereleaseNote}`);
        }
        // filter out by platform
        let components = this.parseReleases(componentReleases);
        const selectedComponent = (0, version_selector_1.selectVersion)(version, components);
        if (selectedComponent) {
            this.logger.debug(`Selected component: ${selectedComponent.name} ${selectedComponent.fullVersion} for platform ${selectedComponent.platform} ${selectedComponent.arch || ''}`);
            return selectedComponent;
        }
        throw new error_handler_1.CLIError(`Component not found for ${componentName}@${version})`, {
            suggestions: [
                `You are trying to install ${componentName}@${version} - check that the component name and version are correct`,
                `Use iocd components browse" to see available components and versions`
            ]
        });
    }
    /**
     * Get all releases without parsing them into components
     * Will filter out pre-releases if usePrereleases is false
     */
    async getAllReleases() {
        if (this.cachedReleases) {
            this.logger.debug(`Using cached releases from GitHub`);
            return this.cachedReleases;
        }
        this.logger.debug(`Fetching all releases from GitHub...`);
        const allReleases = [];
        let nextUrl = `${this.baseUrl}?per_page=100&page=1`;
        while (nextUrl) {
            const response = await fetch(nextUrl);
            if (!response.ok) {
                throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
            }
            const releases = await response.json();
            if (releases.length === 0) {
                break;
            }
            allReleases.push(...releases);
            const linkHeader = response.headers.get('link');
            nextUrl = this.getNextPageUrl(linkHeader);
            // Safety limit
            if (allReleases.length > 1000) {
                break;
            }
        }
        // Filter out pre-releases if usePrereleases is false
        const filteredReleases = this.usePrereleases
            ? allReleases
            : allReleases.filter(release => !release.prerelease);
        this.cachedReleases = filteredReleases;
        return filteredReleases;
    }
    /**
     * Extract the next page URL from the Link header
     * Link header format: <https://api.github.com/repos/owner/repo/releases?page=2>; rel="next", <https://api.github.com/repos/owner/repo/releases?page=5>; rel="last"
     */
    getNextPageUrl(linkHeader) {
        if (!linkHeader) {
            return null;
        }
        const links = linkHeader.split(',');
        for (const link of links) {
            const match = link.match(/<([^>]+)>;\s*rel="next"/);
            if (match) {
                return match[1];
            }
        }
        return null;
    }
    /** Parse GitHub release information into component metadata */
    parseReleases(releases) {
        this.logger.debug(`Parsing ${releases.length} releases into components...`);
        const components = [];
        for (const release of releases) {
            try {
                this.logger.debug(`Processing release: ${release.name} (tag: ${release.tag_name})`);
                const { name, baseVersion, version, buildNumber, platform, arch } = platform_utils_1.ComponentPlatformUtils.parseComponentName(release.tag_name);
                for (const asset of release.assets) {
                    this.logger.debug(`  Found asset: ${asset.name}`);
                    const assetInfo = platform_utils_1.ComponentPlatformUtils.parseComponentFilename(asset.name);
                    components.push({
                        name,
                        version: baseVersion,
                        buildNumber: buildNumber,
                        fullVersion: version,
                        platform: assetInfo.platform || platform || platform_utils_1.ComponentPlatformUtils.getCurrentPlatform(),
                        arch: assetInfo.arch || arch || process.arch,
                        downloadUrl: asset.browser_download_url,
                        filename: asset.name,
                        prerelease: release.prerelease
                    });
                }
            }
            catch (err) {
                this.logger.warn(`Skipping release with unrecognized format: ${release.name}. Error: ${err.message}`);
            }
        }
        return components;
    }
}
exports.GitHubComponentsStore = GitHubComponentsStore;
//# sourceMappingURL=github.store.js.map