/**
 * Startup-owned runtime package update detection and cache state.
 *
 * Reads a small local cache immediately, optionally refreshes it in the
 * background, and returns the update state for this run.
 */
import { mkdirSync, readFileSync, writeFileSync } from 'fs';
import { dirname } from 'path';
import { getUpdateCachePath, getUpdateCommand, getUpdateInstruction, NPM_PACKAGE_NAME, NPM_REGISTRY_URL, VERSION } from '../config.js';
const UPDATE_CACHE_TTL_MS = 20 * 60 * 60 * 1000;
export async function resolveStartupUpdateStatus(options = {}, deps = {}) {
    const cachePath = options.cachePath ?? getUpdateCachePath();
    const currentVersion = options.currentVersion ?? VERSION;
    const packageName = options.packageName ?? NPM_PACKAGE_NAME;
    const registryUrl = options.registryUrl ?? NPM_REGISTRY_URL;
    const now = options.now ?? new Date();
    if (options.skipCheck) {
        return { kind: 'none', currentVersion };
    }
    const cache = readUpdateCache(cachePath);
    // Cold-start users migrated from the old live-check flow do not have an
    // update cache yet. Do one bounded foreground fetch so the first launch
    // after the rollout can still show the update prompt immediately.
    if (!options.offline && !cache) {
        const refreshed = await fetchAndPersistUpdateCache({ cachePath, registryUrl, now }, { fetchLatestVersion: deps.fetchLatestVersion });
        return getUpdateStatusFromCache(refreshed, {
            currentVersion,
            packageName
        });
    }
    if (!options.offline && shouldRefreshUpdateCache(cache, now)) {
        const scheduleBackgroundTask = deps.scheduleBackgroundTask ??
            ((task) => {
                void task().catch(() => { });
            });
        scheduleBackgroundTask(async () => {
            await refreshUpdateCache({ cachePath, registryUrl, now }, { fetchLatestVersion: deps.fetchLatestVersion });
        });
    }
    return getUpdateStatusFromCache(cache, {
        currentVersion,
        packageName
    });
}
export function shouldRefreshUpdateCache(cache, now, ttlMs = UPDATE_CACHE_TTL_MS) {
    if (!cache) {
        return true;
    }
    const lastCheckedAtMs = Date.parse(cache.lastCheckedAt);
    if (!Number.isFinite(lastCheckedAtMs)) {
        return true;
    }
    return now.getTime() - lastCheckedAtMs >= ttlMs;
}
export function getUpdateStatusFromCache(cache, options) {
    const noUpdate = {
        kind: 'none',
        currentVersion: options.currentVersion,
        lastCheckedAt: cache?.lastCheckedAt
    };
    if (!cache) {
        return noUpdate;
    }
    if (!isNewerVersion(cache.latestVersion, options.currentVersion)) {
        return noUpdate;
    }
    const packageName = options.packageName ?? NPM_PACKAGE_NAME;
    return {
        kind: 'available',
        currentVersion: options.currentVersion,
        latestVersion: cache.latestVersion,
        instruction: getUpdateInstruction(packageName),
        command: getUpdateCommand(packageName),
        lastCheckedAt: cache.lastCheckedAt
    };
}
async function refreshUpdateCache(options, deps = {}) {
    const nextCache = await fetchAndPersistUpdateCache(options, deps);
    if (!nextCache) {
        return;
    }
}
async function fetchAndPersistUpdateCache(options, deps = {}) {
    const fetchLatestVersion = deps.fetchLatestVersion ?? fetchLatestVersionFromRegistry;
    const latestVersion = await fetchLatestVersion(options.registryUrl);
    if (!latestVersion) {
        return undefined;
    }
    const nextCache = {
        latestVersion,
        lastCheckedAt: options.now.toISOString()
    };
    writeUpdateCache(options.cachePath, nextCache);
    return nextCache;
}
async function fetchLatestVersionFromRegistry(registryUrl) {
    const response = await fetch(registryUrl, { signal: AbortSignal.timeout(10_000) });
    if (!response.ok) {
        return undefined;
    }
    const data = (await response.json());
    return typeof data.version === 'string' && data.version.trim() ? data.version : undefined;
}
function readUpdateCache(cachePath) {
    try {
        const raw = readFileSync(cachePath, 'utf-8');
        const parsed = JSON.parse(raw);
        if (typeof parsed.latestVersion !== 'string' ||
            !parsed.latestVersion ||
            typeof parsed.lastCheckedAt !== 'string' ||
            !parsed.lastCheckedAt) {
            return undefined;
        }
        return {
            latestVersion: parsed.latestVersion,
            lastCheckedAt: parsed.lastCheckedAt
        };
    }
    catch {
        return undefined;
    }
}
function writeUpdateCache(cachePath, cache) {
    mkdirSync(dirname(cachePath), { recursive: true });
    writeFileSync(cachePath, `${JSON.stringify(cache, null, 2)}\n`, 'utf-8');
}
function isNewerVersion(latestVersion, currentVersion) {
    const latest = parseVersion(latestVersion);
    const current = parseVersion(currentVersion);
    if (!latest || !current) {
        return false;
    }
    for (let index = 0; index < latest.length; index += 1) {
        if (latest[index] > current[index]) {
            return true;
        }
        if (latest[index] < current[index]) {
            return false;
        }
    }
    return false;
}
function parseVersion(version) {
    const parts = version.trim().split('.');
    if (parts.length !== 3) {
        return undefined;
    }
    const parsed = parts.map((part) => Number(part));
    if (parsed.some((part) => !Number.isInteger(part) || part < 0)) {
        return undefined;
    }
    return parsed;
}
//# sourceMappingURL=update-manager.js.map