(() => {
  const GITHUB_OWNER = 'QLHazyCoder';
  const GITHUB_REPO = 'codex-oauth-automation-extension';
  const RELEASES_PAGE_URL = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/releases`;
  const RELEASES_API_URL = `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases?per_page=10`;
  const CACHE_KEY = 'multipage-release-snapshot-v1';
  const IGNORED_UPDATE_VERSION_KEY = 'multipage-ignored-release-version-v1';
  const CACHE_TTL_MS = 60 * 60 * 1000;
  const FETCH_TIMEOUT_MS = 8000;
  const MAX_RELEASES = 10;
  const MAX_NOTES_PER_RELEASE = 5;
  const VERSION_FAMILY_ULTRA = 'ultra';
  const VERSION_FAMILY_PRO = 'pro';
  const VERSION_FAMILY_LEGACY = 'legacy';

  function getVersionFamily(version, fallbackFamily = VERSION_FAMILY_LEGACY) {
    const trimmed = String(version || '').trim();
    if (/^ultra/i.test(trimmed)) {
      return VERSION_FAMILY_ULTRA;
    }
    if (/^pro/i.test(trimmed)) {
      return VERSION_FAMILY_PRO;
    }
    if (/^v/i.test(trimmed)) {
      return VERSION_FAMILY_LEGACY;
    }
    return fallbackFamily;
  }

  function stripVersionPrefix(version) {
    return String(version || '').trim().replace(/^(?:ultra|pro|v)\s*/i, '');
  }

  function extractVersionCore(version) {
    const core = stripVersionPrefix(version).split('-')[0];
    return /^\d+(?:\.\d+){1,3}$/.test(core) ? core : '';
  }

  function formatDisplayVersion(version, fallbackFamily = VERSION_FAMILY_LEGACY) {
    const core = extractVersionCore(version);
    if (!core) {
      return '';
    }

    const family = getVersionFamily(version, fallbackFamily);
    return `${getVersionFamilyPrefix(family)}${core}`;
  }

  function parseVersionMeta(version, fallbackFamily = VERSION_FAMILY_LEGACY) {
    const family = getVersionFamily(version, fallbackFamily);
    const core = extractVersionCore(version);
    return {
      family,
      core,
      displayVersion: core ? `${getVersionFamilyPrefix(family)}${core}` : '',
      parts: core
        ? core.split('.').map((part) => {
          const numeric = Number.parseInt(part, 10);
          return Number.isFinite(numeric) ? numeric : 0;
        })
        : [0],
    };
  }

  function getVersionFamilyPrefix(family) {
    if (family === VERSION_FAMILY_ULTRA) {
      return 'Ultra';
    }
    if (family === VERSION_FAMILY_PRO) {
      return 'Pro';
    }
    return 'v';
  }

  function getVersionFamilyRank(family) {
    if (family === VERSION_FAMILY_ULTRA) {
      return 3;
    }
    if (family === VERSION_FAMILY_PRO) {
      return 2;
    }
    return 1;
  }

  function parseVersionParts(version, fallbackFamily = VERSION_FAMILY_LEGACY) {
    return parseVersionMeta(version, fallbackFamily).parts;
  }

  function compareVersions(left, right, fallbackFamily = VERSION_FAMILY_LEGACY) {
    const leftMeta = parseVersionMeta(left, fallbackFamily);
    const rightMeta = parseVersionMeta(right, fallbackFamily);
    const leftFamilyRank = getVersionFamilyRank(leftMeta.family);
    const rightFamilyRank = getVersionFamilyRank(rightMeta.family);

    if (leftFamilyRank > rightFamilyRank) {
      return 1;
    }
    if (leftFamilyRank < rightFamilyRank) {
      return -1;
    }

    const leftParts = leftMeta.parts;
    const rightParts = rightMeta.parts;
    const maxLength = Math.max(leftParts.length, rightParts.length, 3);

    for (let index = 0; index < maxLength; index += 1) {
      const leftPart = leftParts[index] || 0;
      const rightPart = rightParts[index] || 0;
      if (leftPart > rightPart) {
        return 1;
      }
      if (leftPart < rightPart) {
        return -1;
      }
    }

    return 0;
  }

  function sanitizeInlineMarkdown(text) {
    return String(text || '')
      .replace(/!\[[^\]]*]\(([^)]+)\)/g, '')
      .replace(/\[([^\]]+)]\(([^)]+)\)/g, '$1')
      .replace(/`([^`]+)`/g, '$1')
      .replace(/[*_~>#]/g, '')
      .replace(/\s+/g, ' ')
      .trim();
  }

  function parseReleaseNotes(body) {
    const lines = String(body || '')
      .replace(/\r\n/g, '\n')
      .split('\n')
      .map((line) => line.trim())
      .filter(Boolean);

    const bulletLines = [];
    const plainLines = [];

    for (const line of lines) {
      if (/^#{1,6}\s*/.test(line)) {
        continue;
      }

      if (/^```/.test(line) || /^---+$/.test(line)) {
        continue;
      }

      if (/^[-*+]\s+/.test(line)) {
        bulletLines.push(line.replace(/^[-*+]\s+/, ''));
        continue;
      }

      if (/^\d+\.\s+/.test(line)) {
        bulletLines.push(line.replace(/^\d+\.\s+/, ''));
        continue;
      }

      plainLines.push(line);
    }

    const noteLines = bulletLines.length > 0 ? bulletLines : plainLines;
    return noteLines
      .map(sanitizeInlineMarkdown)
      .filter(Boolean)
      .slice(0, MAX_NOTES_PER_RELEASE);
  }

  function normalizeReleaseVersion(release) {
    const candidates = [
      release?.tag_name,
      release?.name,
    ];

    for (const candidate of candidates) {
      const meta = parseVersionMeta(candidate);
      if (meta.core) {
        return meta;
      }
    }

    return null;
  }

  function sanitizeRelease(release) {
    const versionMeta = normalizeReleaseVersion(release);
    if (!versionMeta?.core) {
      return null;
    }

    const rawTitle = sanitizeInlineMarkdown(release?.name || '');
    const titleMeta = parseVersionMeta(rawTitle, versionMeta.family);
    const normalizedTitle = titleMeta.core && titleMeta.displayVersion === versionMeta.displayVersion
      ? ''
      : rawTitle;

    return {
      version: versionMeta.core,
      displayVersion: versionMeta.displayVersion,
      family: versionMeta.family,
      title: normalizedTitle,
      url: String(release?.html_url || RELEASES_PAGE_URL),
      publishedAt: String(release?.published_at || release?.created_at || ''),
      notes: parseReleaseNotes(release?.body || ''),
    };
  }

  function getComparableReleaseVersion(release) {
    const fallbackFamily = [VERSION_FAMILY_ULTRA, VERSION_FAMILY_PRO].includes(release?.family)
      ? release.family
      : VERSION_FAMILY_LEGACY;
    const displayVersion = String(release?.displayVersion || '').trim();
    if (displayVersion) {
      return displayVersion;
    }
    return formatDisplayVersion(release?.version || '', fallbackFamily);
  }

  function sortReleasesByVersion(releases = []) {
    return [...releases].sort((left, right) => (
      compareVersions(
        getComparableReleaseVersion(right),
        getComparableReleaseVersion(left)
      )
    ));
  }

  function readCache() {
    try {
      const raw = localStorage.getItem(CACHE_KEY);
      if (!raw) {
        return null;
      }

      const parsed = JSON.parse(raw);
      if (!parsed || !Array.isArray(parsed.releases) || !Number.isFinite(parsed.fetchedAt)) {
        return null;
      }

      if ((Date.now() - parsed.fetchedAt) > CACHE_TTL_MS) {
        return null;
      }

      return sortReleasesByVersion(parsed.releases).slice(0, MAX_RELEASES);
    } catch (error) {
      return null;
    }
  }

  function writeCache(releases) {
    try {
      localStorage.setItem(CACHE_KEY, JSON.stringify({
        fetchedAt: Date.now(),
        releases,
      }));
    } catch (error) {
      // Ignore cache write failures.
    }
  }

  function getIgnoredUpdateVersion() {
    try {
      return String(localStorage.getItem(IGNORED_UPDATE_VERSION_KEY) || '').trim();
    } catch (error) {
      return '';
    }
  }

  function setIgnoredUpdateVersion(version) {
    const normalized = formatDisplayVersion(version, VERSION_FAMILY_ULTRA) || String(version || '').trim();
    try {
      if (normalized) {
        localStorage.setItem(IGNORED_UPDATE_VERSION_KEY, normalized);
      } else {
        localStorage.removeItem(IGNORED_UPDATE_VERSION_KEY);
      }
    } catch (error) {
      // Ignore localStorage failures.
    }
    return normalized;
  }

  function clearIgnoredUpdateVersion() {
    return setIgnoredUpdateVersion('');
  }

  async function fetchReleases() {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);

    try {
      const response = await fetch(RELEASES_API_URL, {
        method: 'GET',
        headers: {
          Accept: 'application/vnd.github+json',
        },
        cache: 'no-store',
        signal: controller.signal,
      });

      if (!response.ok) {
        throw new Error(`GitHub Releases 请求失败（${response.status}）`);
      }

      const payload = await response.json();
      if (!Array.isArray(payload)) {
        throw new Error('GitHub Releases 返回格式异常');
      }

      const releases = payload
        .filter((release) => release && !release.draft && !release.prerelease)
        .map(sanitizeRelease)
        .filter(Boolean);

      const sortedReleases = sortReleasesByVersion(releases).slice(0, MAX_RELEASES);

      writeCache(sortedReleases);
      return sortedReleases;
    } catch (error) {
      if (error?.name === 'AbortError') {
        throw new Error('GitHub Releases 请求超时');
      }
      throw error;
    } finally {
      clearTimeout(timeoutId);
    }
  }

  async function loadReleases(options = {}) {
    if (!options.force) {
      const cached = readCache();
      if (cached) {
        return cached;
      }
    }

    return fetchReleases();
  }

  function buildReleaseSnapshot(releases, localVersion) {
    const latestRelease = releases[0] || null;
    if (!latestRelease) {
      return {
        status: 'empty',
        localVersion,
        latestVersion: null,
        latestRelease: null,
        newerReleases: [],
        logUrl: RELEASES_PAGE_URL,
        releasesPageUrl: RELEASES_PAGE_URL,
        checkedAt: Date.now(),
      };
    }

    const newerReleases = releases.filter((release) => compareVersions(release.displayVersion, localVersion) > 0);
    const ignoredVersion = getIgnoredUpdateVersion();
    const newestUpdateVersion = newerReleases[0]?.displayVersion || '';
    const updateIgnored = Boolean(newestUpdateVersion)
      && Boolean(ignoredVersion)
      && compareVersions(ignoredVersion, newestUpdateVersion) === 0;
    return {
      status: newerReleases.length > 0
        ? (updateIgnored ? 'ignored' : 'update-available')
        : 'latest',
      localVersion,
      latestVersion: latestRelease.displayVersion,
      latestRelease,
      newerReleases,
      ignoredVersion: updateIgnored ? ignoredVersion : '',
      logUrl: latestRelease.url || RELEASES_PAGE_URL,
      releasesPageUrl: RELEASES_PAGE_URL,
      checkedAt: Date.now(),
    };
  }

  function getSnapshotUpdateVersion(snapshot = {}) {
    const newerReleases = Array.isArray(snapshot?.newerReleases) ? snapshot.newerReleases : [];
    return newerReleases[0]?.displayVersion || snapshot?.latestVersion || '';
  }

  function ignoreReleaseSnapshot(snapshot = {}) {
    const targetVersion = getSnapshotUpdateVersion(snapshot);
    if (!targetVersion) {
      return '';
    }
    return setIgnoredUpdateVersion(targetVersion);
  }

  function getLocalVersionLabel(manifest = chrome.runtime.getManifest()) {
    const versionName = formatDisplayVersion(manifest?.version_name, VERSION_FAMILY_ULTRA);
    if (versionName) {
      return versionName;
    }

    const versionCore = extractVersionCore(manifest?.version || '');
    return versionCore ? formatDisplayVersion(`Ultra${versionCore}`, VERSION_FAMILY_ULTRA) : '';
  }

  async function getReleaseSnapshot(options = {}) {
    const localVersion = getLocalVersionLabel(chrome.runtime.getManifest()) || 'Ultra0.0';

    try {
      const releases = await loadReleases(options);
      return buildReleaseSnapshot(releases, localVersion);
    } catch (error) {
      return {
        status: 'error',
        localVersion,
        latestVersion: null,
        latestRelease: null,
        newerReleases: [],
        logUrl: RELEASES_PAGE_URL,
        releasesPageUrl: RELEASES_PAGE_URL,
        checkedAt: Date.now(),
        errorMessage: error?.message || '更新检查失败',
      };
    }
  }

  function formatReleaseDate(value) {
    if (!value) {
      return '';
    }

    const date = new Date(value);
    if (Number.isNaN(date.getTime())) {
      return '';
    }

    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    return `${year}-${month}-${day}`;
  }

  window.SidepanelUpdateService = {
    compareVersions,
    formatDisplayVersion,
    formatReleaseDate,
    clearIgnoredUpdateVersion,
    getIgnoredUpdateVersion,
    getLocalVersionLabel,
    getReleaseSnapshot,
    ignoreReleaseSnapshot,
    releasesPageUrl: RELEASES_PAGE_URL,
    stripVersionPrefix,
  };
})();
