import { describe, it, expect, vi, afterEach } from "vitest";
import { isNewer, checkUpdate, openExternal, APP_VERSION } from "./version";

describe("isNewer（语义化版本比较）", () => {
  it.each([
    ["1.0.1", "1.0.0", true],
    ["1.1.0", "1.0.9", true],
    ["2.0.0", "1.9.9", true],
    ["1.0.0", "1.0.0", false],
    ["1.0.0", "1.0.1", false],
    ["0.9.9", "1.0.0", false],
  ])("isNewer(%s, %s) === %s", (a, b, want) => {
    expect(isNewer(a as string, b as string)).toBe(want);
  });

  it("缺省的 patch 位按 0 处理", () => {
    expect(isNewer("1.1", "1.0.9")).toBe(true);
    expect(isNewer("1.0", "1.0.0")).toBe(false);
  });
});

describe("checkUpdate", () => {
  afterEach(() => vi.unstubAllGlobals());

  function stubFetch(impl: () => Promise<unknown> | unknown) {
    vi.stubGlobal("fetch", vi.fn().mockImplementation(impl));
  }

  it("有更新版本 → 返回 ReleaseInfo", async () => {
    stubFetch(() => ({
      ok: true,
      json: async () => ({ tag_name: "v9.9.9", html_url: "https://x/rel", body: "日志" }),
    }));
    const r = await checkUpdate();
    expect(r).toEqual({ version: "9.9.9", url: "https://x/rel", notes: "日志" });
  });

  it("同版本 → null", async () => {
    stubFetch(() => ({ ok: true, json: async () => ({ tag_name: `v${APP_VERSION}` }) }));
    expect(await checkUpdate()).toBeNull();
  });

  it("更旧版本 → null", async () => {
    stubFetch(() => ({ ok: true, json: async () => ({ tag_name: "v0.0.1" }) }));
    expect(await checkUpdate()).toBeNull();
  });

  it("HTTP 非 2xx（404/403 限流）→ 静默 null", async () => {
    stubFetch(() => ({ ok: false, status: 404 }));
    expect(await checkUpdate()).toBeNull();
  });

  it("网络异常（离线）→ 静默 null", async () => {
    stubFetch(() => {
      throw new Error("offline");
    });
    expect(await checkUpdate()).toBeNull();
  });
});

describe("openExternal", () => {
  afterEach(() => {
    delete (window as unknown as { runtime?: unknown }).runtime;
    vi.restoreAllMocks();
  });

  it("有 Wails runtime → 走 BrowserOpenURL", () => {
    const spy = vi.fn();
    (window as unknown as { runtime: { BrowserOpenURL: (u: string) => void } }).runtime = {
      BrowserOpenURL: spy,
    };
    openExternal("https://a");
    expect(spy).toHaveBeenCalledWith("https://a");
  });

  it("无 runtime（浏览器模式）→ 回退 window.open", () => {
    const open = vi.spyOn(window, "open").mockImplementation(() => null);
    openExternal("https://b");
    expect(open).toHaveBeenCalledWith("https://b", "_blank");
  });
});
