/**
 * Checks the Forgejo repo for newer releases and compares against the running
 * package.json version. Results are cached in memory (1 hour for success,
 * 5 minutes for errors) so the status page stays fast under load.
 */

// import required so Node type-strips this .cts (it has no other import).
import type { Db } from '../types/db';

const { getSetting } = require('../db/database.cts');

const REPO_API_BASE =
  process.env.REPO_API_URL || 'https://dream.scheller.ltd/api/v1/repos/null/BillTracker';

// QA-B16-01: the version check is opt-out-able. Admins can disable the external
// request via the `update_check_enabled` setting (default on). When off, no
// network call is made and a `disabled` status is returned.
function updateCheckEnabled(): boolean {
  return getSetting('update_check_enabled') !== 'false';
}

const TTL_OK_MS = 60 * 60 * 1000; // 1 hour on success
const TTL_ERROR_MS = 5 * 60 * 1000; // 5 min on error (avoid hammering)
const FETCH_TIMEOUT_MS = 8_000;

let _cache: { result: any; expiresAt: number } = { result: null, expiresAt: 0 };
let _pkg: any = null;

function getCurrentVersion(): string {
  if (!_pkg) {
    try {
      _pkg = require('../package.json');
    } catch {
      _pkg = { version: '0.0.0' };
    }
  }
  return _pkg.version;
}

// Returns positive if a > b, negative if a < b, 0 if equal.
function compareVersions(a: string, b: string): number {
  const parse = (v: string) => String(v).replace(/^v/, '').split('.').map(Number);
  const pa = parse(a),
    pb = parse(b);
  for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
    const diff = (pa[i] || 0) - (pb[i] || 0);
    if (diff !== 0) return diff;
  }
  return 0;
}

/**
 * @param force  Skip the cache and always hit the API.
 * @returns Update status object.
 */
async function checkForUpdates(force = false): Promise<any> {
  const now = Date.now();

  // Opt-out: no external request when disabled by the admin.
  if (!updateCheckEnabled()) {
    return {
      current_version: getCurrentVersion(),
      latest_version: null,
      up_to_date: null,
      has_update: false,
      latest_release_url: null,
      published_at: null,
      last_checked_at: null,
      error: null,
      disabled: true,
      cached: false,
    };
  }

  if (!force && _cache.result && now < _cache.expiresAt) {
    return { ..._cache.result, cached: true };
  }

  const currentVersion = getCurrentVersion();
  const checkedAt = new Date().toISOString();

  try {
    const res = await fetch(`${REPO_API_BASE}/releases/latest`, {
      headers: {
        Accept: 'application/json',
        'User-Agent': `BillTracker/${currentVersion} UpdateCheck`,
      },
      signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
    });

    // 404 = no releases published yet — treat as up-to-date
    if (res.status === 404) {
      const result = {
        current_version: currentVersion,
        latest_version: null,
        up_to_date: true,
        has_update: false,
        latest_release_url: null,
        published_at: null,
        last_checked_at: checkedAt,
        error: null,
        cached: false,
      };
      _cache = { result, expiresAt: now + TTL_OK_MS };
      return result;
    }

    if (!res.ok) throw new Error(`Forgejo API returned HTTP ${res.status}`);

    const release: any = await res.json();
    const rawTag = release.tag_name || '';
    const latestVersion = rawTag.replace(/^v/, '') || null;
    const hasUpdate = latestVersion ? compareVersions(currentVersion, latestVersion) < 0 : false;

    const result = {
      current_version: currentVersion,
      latest_version: latestVersion,
      up_to_date: !hasUpdate,
      has_update: hasUpdate,
      latest_release_url: release.html_url || null,
      published_at: release.published_at || null,
      last_checked_at: checkedAt,
      error: null,
      cached: false,
    };

    _cache = { result, expiresAt: now + TTL_OK_MS };
    return result;
  } catch (err: any) {
    const result = {
      current_version: currentVersion,
      latest_version: null,
      up_to_date: null, // unknown — network or API failure
      has_update: false,
      latest_release_url: null,
      published_at: null,
      last_checked_at: checkedAt,
      error: err.message || 'Update check failed',
      cached: false,
    };
    _cache = { result, expiresAt: now + TTL_ERROR_MS };
    return result;
  }
}

module.exports = { checkForUpdates, updateCheckEnabled };
