/**
 * 检查是否有新版本。
 *
 * 更新源优先级：
 * 1. chrome.storage.local.updateFeedUrl（可选覆盖）
 * 2. 官网 updates.json（Cloudflare Pages）
 * 3. 官网 updates.xml
 * 4. GitHub Releases API（兜底）
 */
const SITE_BASE_URL = 'https://supermark.seedpower.app';
const UPDATE_REPO = 'chaowei2048/SuperMark';
const UPDATE_FEED_JSON_URL = `${SITE_BASE_URL}/updates.json`;
const UPDATES_XML_URL = `${SITE_BASE_URL}/updates.xml`;
const GITHUB_API_LATEST = `https://api.github.com/repos/${UPDATE_REPO}/releases/latest`;
const RELEASE_PAGE_URL = `${SITE_BASE_URL}/#download`;

const CACHE_KEY = 'updateCheckCache';
const CACHE_TTL_MS = 6 * 60 * 60 * 1000;

function t(key, substitutions, fallback = '') {
  try {
    if (typeof window.getLocalizedMessage === 'function') {
      const msg = window.getLocalizedMessage(key, substitutions);
      if (msg && msg !== key) return msg;
    }
    const msg = chrome.i18n.getMessage(key, substitutions);
    if (msg) return msg;
  } catch (_) {
    /* ignore */
  }
  return fallback || key;
}

/** 比较 a 与 b：1=a更新，0=相同，-1=a更旧 */
export function compareVersions(a, b) {
  const normalize = (v) =>
    String(v || '')
      .trim()
      .replace(/^v/i, '')
      .split(/[.+_-]/)
      .map((part) => {
        const n = parseInt(part, 10);
        return Number.isFinite(n) ? n : 0;
      });

  const pa = normalize(a);
  const pb = normalize(b);
  const len = Math.max(pa.length, pb.length);
  for (let i = 0; i < len; i++) {
    const x = pa[i] || 0;
    const y = pb[i] || 0;
    if (x > y) return 1;
    if (x < y) return -1;
  }
  return 0;
}

function getLocalVersion() {
  try {
    return chrome.runtime.getManifest()?.version || '0';
  } catch (_) {
    return '0';
  }
}

function parseVersionFromUpdatesXml(xmlText) {
  if (!xmlText) return null;
  const match =
    xmlText.match(/<updatecheck[^>]*\sversion=['"]([^'"]+)['"]/i) ||
    xmlText.match(/\sversion=['"]([^'"]+)['"][^>]*\/?>/i);
  return match ? match[1].trim() : null;
}

function parseCodebaseFromUpdatesXml(xmlText) {
  if (!xmlText) return null;
  const match = xmlText.match(/<updatecheck[^>]*\scodebase=['"]([^'"]+)['"]/i);
  return match ? match[1].trim() : null;
}

function parseVersionFromRelease(release) {
  if (!release) return null;
  const assets = Array.isArray(release.assets) ? release.assets : [];
  for (const asset of assets) {
    const name = asset?.name || '';
    const m = name.match(/SuperMark-([\d.]+)\.(?:crx|zip)$/i);
    if (m) return m[1];
  }
  if (release.tag_name) {
    return String(release.tag_name).replace(/^v/i, '');
  }
  return null;
}

function pickDownloadUrl(release, latestVersion) {
  const assets = Array.isArray(release?.assets) ? release.assets : [];
  const zip = assets.find((a) => a.name === `SuperMark-${latestVersion}.zip`);
  const crx = assets.find((a) => a.name === `SuperMark-${latestVersion}.crx`);
  return {
    zipUrl: zip?.browser_download_url || null,
    crxUrl: crx?.browser_download_url || null,
    htmlUrl: release?.html_url || RELEASE_PAGE_URL
  };
}

function normalizeFeed(payload, source) {
  const latestVersion = String(payload.version || payload.latestVersion || '').replace(/^v/i, '');
  if (!latestVersion) {
    throw new Error(`Invalid update feed from ${source}`);
  }
  return {
    latestVersion,
    htmlUrl: payload.htmlUrl || payload.html_url || RELEASE_PAGE_URL,
    zipUrl: payload.zipUrl || payload.zip_url || `${SITE_BASE_URL}/download/SuperMark-${latestVersion}.zip`,
    crxUrl: payload.crxUrl || payload.crx_url || `${SITE_BASE_URL}/download/SuperMark-${latestVersion}.crx`,
    source
  };
}

async function fetchJsonFeed(url) {
  const res = await fetch(url, {
    cache: 'no-store',
    headers: { Accept: 'application/json' }
  });
  if (!res.ok) throw new Error(`update json HTTP ${res.status}`);
  return normalizeFeed(await res.json(), url);
}

async function fetchLatestFromXml() {
  const res = await fetch(UPDATES_XML_URL, {
    cache: 'no-store',
    headers: { Accept: 'application/xml,text/xml,*/*' }
  });
  if (!res.ok) throw new Error(`updates.xml HTTP ${res.status}`);
  const text = await res.text();
  const version = parseVersionFromUpdatesXml(text);
  if (!version) throw new Error('Failed to parse updates.xml version');
  const codebase = parseCodebaseFromUpdatesXml(text);
  return {
    latestVersion: version,
    htmlUrl: RELEASE_PAGE_URL,
    zipUrl: `${SITE_BASE_URL}/download/SuperMark-${version}.zip`,
    crxUrl: codebase || `${SITE_BASE_URL}/download/SuperMark-${version}.crx`,
    source: 'updates.xml'
  };
}

async function fetchLatestFromApi() {
  const res = await fetch(GITHUB_API_LATEST, {
    cache: 'no-store',
    headers: {
      Accept: 'application/vnd.github+json',
      'X-GitHub-Api-Version': '2022-11-28'
    }
  });
  if (!res.ok) throw new Error(`GitHub API HTTP ${res.status}`);
  const release = await res.json();
  const latestVersion = parseVersionFromRelease(release);
  if (!latestVersion) throw new Error('Failed to parse release version');
  const downloads = pickDownloadUrl(release, latestVersion);
  return {
    latestVersion,
    ...downloads,
    source: 'github-api'
  };
}

function readCache() {
  return new Promise((resolve) => {
    chrome.storage.local.get([CACHE_KEY, 'updateFeedUrl'], (result) => {
      resolve(result || {});
    });
  });
}

function writeCache(payload) {
  return new Promise((resolve) => {
    chrome.storage.local.set({ [CACHE_KEY]: payload }, resolve);
  });
}

/**
 * @param {{ force?: boolean }} options
 */
export async function checkForUpdate({ force = false } = {}) {
  const localVersion = getLocalVersion();
  const now = Date.now();
  const stored = await readCache();

  if (!force) {
    const cached = stored[CACHE_KEY];
    if (cached && cached.checkedAt && now - cached.checkedAt < CACHE_TTL_MS && cached.localVersion === localVersion) {
      return cached;
    }
  }

  const customFeed = typeof stored.updateFeedUrl === 'string' ? stored.updateFeedUrl.trim() : '';
  const attempts = [];
  if (customFeed) attempts.push(() => fetchJsonFeed(customFeed));
  attempts.push(() => fetchJsonFeed(UPDATE_FEED_JSON_URL));
  attempts.push(fetchLatestFromXml);
  attempts.push(fetchLatestFromApi);

  let remote = null;
  let lastError = null;
  for (const attempt of attempts) {
    try {
      remote = await attempt();
      break;
    } catch (err) {
      lastError = err;
    }
  }

  if (!remote) {
    const result = {
      status: 'error',
      localVersion,
      checkedAt: now,
      message: lastError?.message || 'Update check failed'
    };
    await writeCache(result);
    return result;
  }

  const cmp = compareVersions(remote.latestVersion, localVersion);
  const result = {
    status: cmp > 0 ? 'updateAvailable' : 'upToDate',
    localVersion,
    latestVersion: remote.latestVersion,
    htmlUrl: remote.htmlUrl || RELEASE_PAGE_URL,
    zipUrl: remote.zipUrl || null,
    crxUrl: remote.crxUrl || null,
    checkedAt: now,
    source: remote.source
  };
  await writeCache(result);
  return result;
}

export function formatUpdateStatus(result) {
  if (!result) return '';
  if (result.status === 'updateAvailable') {
    return t(
      'updateAvailableMessage',
      [result.latestVersion],
      `New version ${result.latestVersion} is available`
    );
  }
  if (result.status === 'upToDate') {
    return t('updateUpToDateMessage', null, 'You are on the latest version');
  }
  return t('updateCheckFailedMessage', null, 'Could not check for updates');
}

export const UpdateCheckConfig = {
  SITE_BASE_URL,
  UPDATE_REPO,
  UPDATE_FEED_JSON_URL,
  UPDATES_XML_URL,
  RELEASE_PAGE_URL,
  CACHE_KEY
};
