const ANTIGRAVITY_RELEASE_FEED_URL =
  "https://antigravity-auto-updater-974169037036.us-central1.run.app/releases";
const ANTIGRAVITY_GITHUB_RELEASE_URL =
  "https://api.github.com/repos/antigravityide/antigravity/releases/latest";

export const ANTIGRAVITY_VERSION_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
export const ANTIGRAVITY_VERSION_FETCH_TIMEOUT_MS = 5_000;
// Floor version synced with Antigravity-Manager v4.2.0 KNOWN_STABLE_VERSION.
export const ANTIGRAVITY_FALLBACK_VERSION = "4.2.0";

type VersionCache = {
  fetchedAt: number;
  version: string;
};

type FetchLike = typeof fetch;

let versionCache: VersionCache | null = null;
let inFlightRequest: Promise<string> | null = null;

function normalizeVersion(value: unknown): string | null {
  if (typeof value !== "string") return null;
  const trimmed = value.trim().replace(/^v/i, "");
  const match = trimmed.match(/^(\d+\.\d+\.\d+)\b/);
  return match ? match[1] : null;
}

function compareSemver(a: string, b: string): number {
  const aParts = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
  const bParts = b.split(".").map((part) => Number.parseInt(part, 10) || 0);
  for (let i = 0; i < 3; i += 1) {
    if (aParts[i] !== bParts[i]) return aParts[i] - bParts[i];
  }
  return 0;
}

function pickNewestVersion(...versions: Array<string | null | undefined>): string {
  return versions
    .map((version) => normalizeVersion(version))
    .filter((version): version is string => !!version)
    .reduce(
      (best, version) => (compareSemver(version, best) > 0 ? version : best),
      ANTIGRAVITY_FALLBACK_VERSION
    );
}

async function fetchJsonWithTimeout(fetchImpl: FetchLike, url: string): Promise<unknown> {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), ANTIGRAVITY_VERSION_FETCH_TIMEOUT_MS);

  try {
    const response = await fetchImpl(url, {
      headers: {
        Accept: "application/json",
        "User-Agent": "OmniRoute-AntigravityVersion/1.0",
      },
      signal: controller.signal,
    });

    if (!response.ok) {
      throw new Error(`Version source ${url} returned ${response.status}`);
    }

    return response.json();
  } finally {
    clearTimeout(timeoutId);
  }
}

function parseOfficialReleaseFeed(payload: unknown): string | null {
  if (!Array.isArray(payload)) return null;

  for (const entry of payload) {
    const version = normalizeVersion((entry as { version?: unknown })?.version);
    if (version) return version;
  }

  return null;
}

function parseGitHubRelease(payload: unknown): string | null {
  if (!payload || typeof payload !== "object") return null;

  const candidate =
    (payload as { tag_name?: unknown }).tag_name ?? (payload as { name?: unknown }).name;

  return normalizeVersion(candidate);
}

async function fetchLatestAntigravityVersion(fetchImpl: FetchLike): Promise<string | null> {
  const sources = [
    {
      parse: parseOfficialReleaseFeed,
      url: ANTIGRAVITY_RELEASE_FEED_URL,
    },
    {
      parse: parseGitHubRelease,
      url: ANTIGRAVITY_GITHUB_RELEASE_URL,
    },
  ];

  for (const source of sources) {
    try {
      const payload = await fetchJsonWithTimeout(fetchImpl, source.url);
      const version = source.parse(payload);
      if (version) return version;
    } catch {
      // Try the next source and fall back to the last known good version if all fail.
    }
  }

  return null;
}

export async function resolveAntigravityVersion(fetchImpl: FetchLike = fetch): Promise<string> {
  const now = Date.now();

  if (versionCache && now - versionCache.fetchedAt < ANTIGRAVITY_VERSION_CACHE_TTL_MS) {
    return versionCache.version;
  }

  if (inFlightRequest) {
    return inFlightRequest;
  }

  inFlightRequest = (async () => {
    const resolved = await fetchLatestAntigravityVersion(fetchImpl);
    const version = resolved
      ? pickNewestVersion(resolved, ANTIGRAVITY_FALLBACK_VERSION)
      : pickNewestVersion(versionCache?.version, ANTIGRAVITY_FALLBACK_VERSION);

    if (resolved) {
      versionCache = {
        fetchedAt: Date.now(),
        version,
      };
    }

    return version;
  })();

  try {
    return await inFlightRequest;
  } finally {
    inFlightRequest = null;
  }
}

export function getCachedAntigravityVersion(): string {
  return versionCache?.version || ANTIGRAVITY_FALLBACK_VERSION;
}

export function seedAntigravityVersionCache(version: string, fetchedAt = Date.now()): void {
  versionCache = {
    fetchedAt,
    version,
  };
}

export function clearAntigravityVersionCache(): void {
  versionCache = null;
  inFlightRequest = null;
}
