import { execFileSync } from "child_process";
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
import { join } from "path";
import { homedir } from "os";
import { logger } from "../monitoring/logger.js";
import {
  ErrorCode,
  getErrorMessage,
  wrapError
} from "../errors/index.js";
import { withTimeout, gracefulDegrade } from "../errors/recovery.js";
class UpdateChecker {
  static CACHE_FILE = join(
    homedir(),
    ".stackmemory",
    "update-check.json"
  );
  static CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
  // 24 hours
  static PACKAGE_NAME = "@stackmemoryai/stackmemory";
  /**
   * Check for updates and display notification if needed
   */
  static async checkForUpdates(currentVersion, silent = false) {
    try {
      const cache = this.loadCache();
      const now = Date.now();
      if (cache && now - cache.lastChecked < this.CHECK_INTERVAL) {
        if (!silent && cache.latestVersion && cache.latestVersion !== currentVersion) {
          this.displayUpdateNotification(currentVersion, cache.latestVersion);
        }
        return;
      }
      const latestVersion = await this.fetchLatestVersion();
      this.saveCache({
        lastChecked: now,
        latestVersion,
        currentVersion
      });
      if (!silent && latestVersion && this.isNewerVersion(currentVersion, latestVersion)) {
        this.displayUpdateNotification(currentVersion, latestVersion);
      }
    } catch (error) {
      const wrappedError = wrapError(
        error,
        "Update check failed",
        ErrorCode.INTERNAL_ERROR,
        { currentVersion, silent }
      );
      logger.debug("Update check failed:", {
        error: getErrorMessage(error),
        context: wrappedError.context
      });
    }
  }
  /**
   * Fetch latest version from npm registry
   */
  static async fetchLatestVersion() {
    try {
      const fetchVersion = async () => {
        const output = execFileSync("npm", ["view", this.PACKAGE_NAME, "version"], {
          encoding: "utf-8",
          stdio: ["pipe", "pipe", "ignore"],
          timeout: 5e3
          // 5 second timeout
        }).trim();
        return output;
      };
      return await gracefulDegrade(
        () => withTimeout(fetchVersion, 5e3, "npm registry timeout"),
        "",
        { operation: "fetchLatestVersion", package: this.PACKAGE_NAME }
      );
    } catch (error) {
      const wrappedError = wrapError(
        error,
        "Failed to fetch latest version from npm",
        ErrorCode.SERVICE_UNAVAILABLE,
        { package: this.PACKAGE_NAME }
      );
      logger.debug("Failed to fetch latest version:", {
        error: getErrorMessage(error),
        context: wrappedError.context
      });
      return "";
    }
  }
  /**
   * Compare version strings
   */
  static isNewerVersion(current, latest) {
    try {
      const currentParts = current.split(".").map(Number);
      const latestParts = latest.split(".").map(Number);
      if (currentParts.some(isNaN) || latestParts.some(isNaN)) {
        logger.debug("Invalid version format:", { current, latest });
        return false;
      }
      for (let i = 0; i < 3; i++) {
        const latestPart = latestParts[i] ?? 0;
        const currentPart = currentParts[i] ?? 0;
        if (latestPart > currentPart) return true;
        if (latestPart < currentPart) return false;
      }
      return false;
    } catch (error) {
      logger.debug("Version comparison failed:", {
        error: getErrorMessage(error),
        current,
        latest
      });
      return false;
    }
  }
  /**
   * Display update notification
   */
  static displayUpdateNotification(current, latest) {
    console.log("\n" + "\u2500".repeat(60));
    console.log("\u{1F4E6} StackMemory Update Available!");
    console.log(`   Current: v${current}`);
    console.log(`   Latest:  v${latest}`);
    console.log("\n   Update with:");
    console.log("   npm install -g @stackmemoryai/stackmemory@latest");
    console.log("\u2500".repeat(60) + "\n");
  }
  /**
   * Load update cache
   */
  static loadCache() {
    try {
      if (!existsSync(this.CACHE_FILE)) {
        return null;
      }
      const data = readFileSync(this.CACHE_FILE, "utf-8");
      const cache = JSON.parse(data);
      if (typeof cache.lastChecked !== "number" || typeof cache.latestVersion !== "string" || typeof cache.currentVersion !== "string") {
        logger.debug("Invalid cache format, ignoring");
        return null;
      }
      return cache;
    } catch (error) {
      const wrappedError = wrapError(
        error,
        "Failed to load update cache",
        ErrorCode.INTERNAL_ERROR,
        { cacheFile: this.CACHE_FILE }
      );
      logger.debug("Failed to load update cache:", {
        error: getErrorMessage(error),
        context: wrappedError.context
      });
      return null;
    }
  }
  /**
   * Save update cache
   */
  static saveCache(cache) {
    try {
      const dir = join(homedir(), ".stackmemory");
      if (!existsSync(dir)) {
        mkdirSync(dir, { recursive: true, mode: 493 });
      }
      const tempFile = `${this.CACHE_FILE}.tmp`;
      writeFileSync(tempFile, JSON.stringify(cache, null, 2), {
        mode: 420
      });
      if (existsSync(this.CACHE_FILE)) {
        writeFileSync(this.CACHE_FILE, JSON.stringify(cache, null, 2));
      } else {
        writeFileSync(this.CACHE_FILE, JSON.stringify(cache, null, 2));
      }
    } catch (error) {
      const wrappedError = wrapError(
        error,
        "Failed to save update cache",
        ErrorCode.INTERNAL_ERROR,
        { cacheFile: this.CACHE_FILE, cache }
      );
      logger.debug("Failed to save update cache:", {
        error: getErrorMessage(error),
        context: wrappedError.context
      });
    }
  }
  /**
   * Force check for updates (ignores cache)
   */
  static async forceCheck(currentVersion) {
    try {
      const latestVersion = await this.fetchLatestVersion();
      this.saveCache({
        lastChecked: Date.now(),
        latestVersion,
        currentVersion
      });
      if (latestVersion) {
        if (this.isNewerVersion(currentVersion, latestVersion)) {
          this.displayUpdateNotification(currentVersion, latestVersion);
        } else {
          console.log(`\u2705 StackMemory is up to date (v${currentVersion})`);
        }
      }
    } catch (error) {
      console.error("\u274C Update check failed:", error.message);
    }
  }
}
export {
  UpdateChecker
};
//# sourceMappingURL=update-checker.js.map
