/**
 * 认证工具测试（token / user 持久化 + login/logout）
 */
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
  getToken,
  setToken,
  clearToken,
  getUser,
  setUser,
  isAuthenticated,
  login,
  logout,
} from "./auth";
import type { CurrentUser } from "@/types/view-models";

const mockUser: CurrentUser = {
  id: "u-test-001",
  email: "test@edu.test",
  name: "测试用户",
  roles: ["system_admin"],
  permissions: ["ADMIN_DASHBOARD_VIEW"],
  dataScope: "ALL",
  schoolId: null,
  schoolName: "Edu 实验学校",
};

describe("auth token utilities", () => {
  beforeEach(() => {
    localStorage.clear();
    vi.mocked(global.fetch).mockReset();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it("setToken / getToken 持久化 token", () => {
    expect(getToken()).toBeNull();
    setToken("mock-token-123");
    expect(getToken()).toBe("mock-token-123");
  });

  it("clearToken 清除 token 和 user", () => {
    setToken("mock-token-123");
    setUser(mockUser);
    clearToken();
    expect(getToken()).toBeNull();
    expect(getUser()).toBeNull();
  });

  it("setUser / getUser 持久化用户信息", () => {
    expect(getUser()).toBeNull();
    setUser(mockUser);
    const user = getUser();
    expect(user).not.toBeNull();
    expect(user?.id).toBe("u-test-001");
    expect(user?.email).toBe("test@edu.test");
  });

  it("getUser 在 JSON 损坏时返回 null", () => {
    localStorage.setItem("edu_user_info", "{invalid json}");
    expect(getUser()).toBeNull();
  });

  it("isAuthenticated 基于 token 存在判断", () => {
    expect(isAuthenticated()).toBe(false);
    setToken("mock-token-123");
    expect(isAuthenticated()).toBe(true);
    clearToken();
    expect(isAuthenticated()).toBe(false);
  });
});

describe("login()", () => {
  beforeEach(() => {
    localStorage.clear();
    vi.mocked(global.fetch).mockReset();
  });

  it("登录成功后持久化 token 和 user", async () => {
    vi.mocked(global.fetch).mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        success: true,
        data: {
          user: mockUser,
          tokens: { accessToken: "jwt-token-success" },
        },
      }),
    } as unknown as Response);

    const result = await login("test@edu.test", "password");
    expect(result.token).toBe("jwt-token-success");
    expect(result.user.id).toBe("u-test-001");
    expect(getToken()).toBe("jwt-token-success");
    expect(getUser()?.id).toBe("u-test-001");
  });

  it("登录失败抛出错误", async () => {
    vi.mocked(global.fetch).mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        success: false,
        error: { message: "邮箱或密码错误" },
      }),
    } as unknown as Response);

    await expect(login("wrong@edu.test", "wrong-password")).rejects.toThrow(
      "邮箱或密码错误",
    );
    expect(getToken()).toBeNull();
  });

  it("登录失败无 error.message 时使用默认错误信息", async () => {
    vi.mocked(global.fetch).mockResolvedValueOnce({
      ok: true,
      json: async () => ({ success: false }),
    } as unknown as Response);

    await expect(login("a@b.c", "x")).rejects.toThrow("登录失败");
  });
});

describe("logout()", () => {
  beforeEach(() => {
    localStorage.clear();
  });

  it("logout 清除 token 并跳转", () => {
    setToken("mock-token-123");
    setUser(mockUser);
    expect(isAuthenticated()).toBe(true);

    // mock window.location.href 赋值（jsdom 不支持导航）
    const originalHref = window.location.href;
    const hrefSetter = vi.fn();
    Object.defineProperty(window, "location", {
      writable: true,
      value: {
        ...window.location,
        set href(v: string) {
          hrefSetter(v);
        },
      },
    });

    logout();
    expect(isAuthenticated()).toBe(false);

    // 恢复
    Object.defineProperty(window, "location", {
      writable: true,
      value: { href: originalHref },
    });
  });
});
