/**
 * auth store 单元测试
 *
 * 覆盖：初始状态、登录/登出流程、权限提取、localStorage 持久化、状态重置。
 * 所有外部依赖（API 调用、localStorage）均被 mock。
 */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

// ---------------------------------------------------------------------------
// Mock 外部依赖（必须在 import 被测模块之前）
// ---------------------------------------------------------------------------

// Mock localStorage — auth.ts 模块顶层直接读取 localStorage，
// 必须在 vi.mock 工厂中提供实现，否则 atob/jwt 解码会失败。
const localStorageStore: Record<string, string> = {};

vi.stubGlobal('localStorage', {
  getItem: vi.fn((key: string) => localStorageStore[key] ?? null),
  setItem: vi.fn((key: string, value: string) => {
    localStorageStore[key] = value;
  }),
  removeItem: vi.fn((key: string) => {
    delete localStorageStore[key];
  }),
  clear: vi.fn(() => {
    Object.keys(localStorageStore).forEach((k) => delete localStorageStore[k]);
  }),
  get length() {
    return Object.keys(localStorageStore).length;
  },
  key: vi.fn((index: number) => Object.keys(localStorageStore)[index] ?? null),
});

// Mock auth API
const mockApiLogin = vi.fn();
const mockApiLogout = vi.fn();

vi.mock('../api/auth', () => ({
  login: (...args: unknown[]) => mockApiLogin(...args),
  logout: (...args: unknown[]) => mockApiLogout(...args),
}));

// Mock clearApiCache
const mockClearApiCache = vi.fn();
vi.mock('../api/client', () => ({
  clearApiCache: (...args: unknown[]) => mockClearApiCache(...args),
}));

// ---------------------------------------------------------------------------
// 在 mock 生效后导入被测模块
// ---------------------------------------------------------------------------
import { useAuthStore } from './auth';
import type { UserInfo, LoginResponse } from '../api/auth';

// ---------------------------------------------------------------------------
// 测试辅助函数
// ---------------------------------------------------------------------------

/** 生成一个包含 permissions 数组的 JWT payload 并编码为合法 token */
function createFakeToken(permissions: string[], extra: Record<string, unknown> = {}): string {
  const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
  const payload = btoa(JSON.stringify({ permissions, ...extra }))
    .replace(/\+/g, '-')
    .replace(/\//g, '_');
  const signature = btoa('fake-signature');
  return `${header}.${payload}.${signature}`;
}

function createFakeUser(overrides: Partial<UserInfo> = {}): UserInfo {
  return {
    id: 'user-001',
    username: 'testuser',
    email: 'test@example.com',
    phone: '13800138000',
    display_name: '测试用户',
    avatar_url: undefined,
    status: 'active',
    roles: [
      { id: 'role-001', name: '管理员', code: 'admin', is_system: true },
    ],
    version: 1,
    ...overrides,
  };
}

function createFakeLoginResponse(overrides: Partial<LoginResponse> = {}): LoginResponse {
  return {
    access_token: createFakeToken(['health.patient.list', 'health.alerts.manage']),
    refresh_token: 'refresh-token-xxx',
    expires_in: 3600,
    user: createFakeUser(),
    ...overrides,
  };
}

// ---------------------------------------------------------------------------
// 测试套件
// ---------------------------------------------------------------------------

describe('useAuthStore', () => {
  beforeEach(() => {
    // 重置所有 mock 调用记录
    vi.clearAllMocks();
    // 清空 localStorage 存储和 store 状态
    Object.keys(localStorageStore).forEach((k) => delete localStorageStore[k]);
    // 将 store 状态重置为未认证
    useAuthStore.setState({
      user: null,
      isAuthenticated: false,
      loading: false,
      permissions: [],
    });
  });

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

  // =========================================================================
  // 初始状态
  // =========================================================================
  describe('初始状态', () => {
    it('应有正确的默认状态字段', () => {
      const state = useAuthStore.getState();
      expect(state).toHaveProperty('user');
      expect(state).toHaveProperty('isAuthenticated');
      expect(state).toHaveProperty('loading');
      expect(state).toHaveProperty('permissions');
      expect(state).toHaveProperty('login');
      expect(state).toHaveProperty('logout');
      expect(state).toHaveProperty('loadFromStorage');
    });

    it('无 token 时应为未认证状态', () => {
      const state = useAuthStore.getState();
      expect(state.isAuthenticated).toBe(false);
      expect(state.user).toBeNull();
      expect(state.permissions).toEqual([]);
      expect(state.loading).toBe(false);
    });
  });

  // =========================================================================
  // 登录流程
  // =========================================================================
  describe('login', () => {
    it('成功登录应更新 user、isAuthenticated、permissions 并写入 localStorage', async () => {
      const fakeUser = createFakeUser();
      const fakeResponse = createFakeLoginResponse({ user: fakeUser });
      mockApiLogin.mockResolvedValue(fakeResponse);

      await useAuthStore.getState().login('testuser', 'password123');

      const state = useAuthStore.getState();

      // store 状态正确
      expect(state.user).toEqual(fakeUser);
      expect(state.isAuthenticated).toBe(true);
      expect(state.loading).toBe(false);
      expect(state.permissions).toEqual(['health.patient.list', 'health.alerts.manage']);

      // API 被正确调用
      expect(mockApiLogin).toHaveBeenCalledWith({ username: 'testuser', password: 'password123' });

      // localStorage 被写入
      expect(localStorage.setItem).toHaveBeenCalledWith('access_token', fakeResponse.access_token);
      expect(localStorage.setItem).toHaveBeenCalledWith('refresh_token', fakeResponse.refresh_token);
      expect(localStorage.setItem).toHaveBeenCalledWith('user', JSON.stringify(fakeUser));

      // API 缓存被清除
      expect(mockClearApiCache).toHaveBeenCalled();
    });

    it('登录中 loading 应为 true，完成后恢复为 false', async () => {
      let resolveLogin: (value: unknown) => void;
      const loginPromise = new Promise((resolve) => {
        resolveLogin = resolve;
      });
      mockApiLogin.mockReturnValue(loginPromise);

      const loginAction = useAuthStore.getState().login('user', 'pass');

      // 登录进行中
      expect(useAuthStore.getState().loading).toBe(true);

      resolveLogin!(createFakeLoginResponse());
      await loginAction;

      expect(useAuthStore.getState().loading).toBe(false);
    });

    it('登录失败应重置 loading 为 false 并向上抛出错误', async () => {
      const error = new Error('Invalid credentials');
      mockApiLogin.mockRejectedValue(error);

      await expect(
        useAuthStore.getState().login('baduser', 'wrongpass'),
      ).rejects.toThrow('Invalid credentials');

      const state = useAuthStore.getState();
      expect(state.loading).toBe(false);
      expect(state.isAuthenticated).toBe(false);
      expect(state.user).toBeNull();

      // 失败不应写入 localStorage
      expect(localStorage.setItem).not.toHaveBeenCalled();
    });

    it('登录失败不应清除 API 缓存', async () => {
      mockApiLogin.mockRejectedValue(new Error('Network error'));

      await expect(
        useAuthStore.getState().login('user', 'pass'),
      ).rejects.toThrow();

      expect(mockClearApiCache).not.toHaveBeenCalled();
    });
  });

  // =========================================================================
  // 登出流程
  // =========================================================================
  describe('logout', () => {
    it('登出应清除所有状态和 localStorage', async () => {
      // 先登录
      const fakeUser = createFakeUser();
      const fakeResponse = createFakeLoginResponse({ user: fakeUser });
      mockApiLogin.mockResolvedValue(fakeResponse);
      await useAuthStore.getState().login('testuser', 'password');

      // 确认已登录
      expect(useAuthStore.getState().isAuthenticated).toBe(true);

      // 登出
      mockApiLogout.mockResolvedValue(undefined);
      await useAuthStore.getState().logout();

      const state = useAuthStore.getState();
      expect(state.user).toBeNull();
      expect(state.isAuthenticated).toBe(false);
      expect(state.permissions).toEqual([]);

      // localStorage 被清除
      expect(localStorage.removeItem).toHaveBeenCalledWith('access_token');
      expect(localStorage.removeItem).toHaveBeenCalledWith('refresh_token');
      expect(localStorage.removeItem).toHaveBeenCalledWith('user');

      // API 缓存被清除
      expect(mockClearApiCache).toHaveBeenCalled();
    });

    it('登出 API 报错时仍应清除本地状态（不抛出错误）', async () => {
      // 先设置一个已认证状态
      useAuthStore.setState({
        user: createFakeUser(),
        isAuthenticated: true,
        permissions: ['health.patient.list'],
      });
      localStorageStore['access_token'] = 'some-token';

      // logout API 失败
      mockApiLogout.mockRejectedValue(new Error('Server unreachable'));

      // 不应抛出
      await expect(useAuthStore.getState().logout()).resolves.toBeUndefined();

      const state = useAuthStore.getState();
      expect(state.user).toBeNull();
      expect(state.isAuthenticated).toBe(false);
      expect(state.permissions).toEqual([]);
      expect(localStorage.removeItem).toHaveBeenCalledWith('access_token');
    });

    it('登出时应调用 logout API', async () => {
      mockApiLogout.mockResolvedValue(undefined);
      await useAuthStore.getState().logout();
      expect(mockApiLogout).toHaveBeenCalledTimes(1);
    });
  });

  // =========================================================================
  // 权限提取（extractPermissions 内部逻辑）
  // =========================================================================
  describe('权限提取', () => {
    it('登录后 permissions 应从 JWT token 中正确解析', async () => {
      const permissions = ['health.patient.list', 'health.alerts.manage', 'health.report.review'];
      const token = createFakeToken(permissions);
      const fakeResponse = createFakeLoginResponse({ access_token: token });
      mockApiLogin.mockResolvedValue(fakeResponse);

      await useAuthStore.getState().login('user', 'pass');

      expect(useAuthStore.getState().permissions).toEqual(permissions);
    });

    it('token 无 permissions 字段时 permissions 应为空数组', async () => {
      // 手动构造一个没有 permissions 的 token
      const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
      const payload = btoa(JSON.stringify({ sub: 'user-001' }))
        .replace(/\+/g, '-')
        .replace(/\//g, '_');
      const signature = btoa('sig');
      const token = `${header}.${payload}.${signature}`;

      const fakeResponse = createFakeLoginResponse({ access_token: token });
      mockApiLogin.mockResolvedValue(fakeResponse);

      await useAuthStore.getState().login('user', 'pass');

      expect(useAuthStore.getState().permissions).toEqual([]);
    });

    it('token 格式非法时 permissions 应为空数组', async () => {
      const fakeResponse = createFakeLoginResponse({ access_token: 'not-a-jwt' });
      mockApiLogin.mockResolvedValue(fakeResponse);

      await useAuthStore.getState().login('user', 'pass');

      expect(useAuthStore.getState().permissions).toEqual([]);
    });

    it('空 permissions 数组应正确解析', async () => {
      const token = createFakeToken([]);
      const fakeResponse = createFakeLoginResponse({ access_token: token });
      mockApiLogin.mockResolvedValue(fakeResponse);

      await useAuthStore.getState().login('user', 'pass');

      expect(useAuthStore.getState().permissions).toEqual([]);
    });
  });

  // =========================================================================
  // localStorage 持久化 / loadFromStorage
  // =========================================================================
  describe('loadFromStorage', () => {
    it('localStorage 有有效 token 和 user 时应恢复认证状态', () => {
      const fakeUser = createFakeUser();
      const permissions = ['health.patient.list'];
      const token = createFakeToken(permissions);

      localStorageStore['access_token'] = token;
      localStorageStore['user'] = JSON.stringify(fakeUser);

      useAuthStore.getState().loadFromStorage();

      const state = useAuthStore.getState();
      expect(state.user).toEqual(fakeUser);
      expect(state.isAuthenticated).toBe(true);
      expect(state.permissions).toEqual(permissions);
    });

    it('localStorage 无 token 时应保持未认证', () => {
      localStorageStore['user'] = JSON.stringify(createFakeUser());
      // 不设置 access_token

      useAuthStore.getState().loadFromStorage();

      const state = useAuthStore.getState();
      expect(state.isAuthenticated).toBe(false);
      expect(state.user).toBeNull();
    });

    it('localStorage 无 user 时应保持未认证', () => {
      localStorageStore['access_token'] = createFakeToken(['health.patient.list']);
      // 不设置 user

      useAuthStore.getState().loadFromStorage();

      const state = useAuthStore.getState();
      expect(state.isAuthenticated).toBe(false);
    });

    it('localStorage user JSON 损坏时应移除损坏数据并保持未认证', () => {
      localStorageStore['access_token'] = createFakeToken([]);
      localStorageStore['user'] = '{invalid json!!!';

      useAuthStore.getState().loadFromStorage();

      const state = useAuthStore.getState();
      expect(state.isAuthenticated).toBe(false);
      expect(state.user).toBeNull();
      // 损坏的 user 数据应被清理
      expect(localStorage.removeItem).toHaveBeenCalledWith('user');
    });

    it('token 和 user 都为空时应返回干净的未认证状态', () => {
      useAuthStore.getState().loadFromStorage();

      const state = useAuthStore.getState();
      expect(state.user).toBeNull();
      expect(state.isAuthenticated).toBe(false);
      expect(state.permissions).toEqual([]);
    });
  });

  // =========================================================================
  // 状态重置验证
  // =========================================================================
  describe('状态重置', () => {
    it('登出后再次登录应正确设置新状态', async () => {
      // 第一次登录
      const user1 = createFakeUser({ id: 'user-001', username: 'user1' });
      const token1 = createFakeToken(['perm.a']);
      mockApiLogin.mockResolvedValue(createFakeLoginResponse({ user: user1, access_token: token1 }));
      await useAuthStore.getState().login('user1', 'pass1');

      expect(useAuthStore.getState().user?.id).toBe('user-001');
      expect(useAuthStore.getState().permissions).toEqual(['perm.a']);

      // 登出
      mockApiLogout.mockResolvedValue(undefined);
      await useAuthStore.getState().logout();

      expect(useAuthStore.getState().user).toBeNull();
      expect(useAuthStore.getState().permissions).toEqual([]);

      // 第二次登录（不同用户、不同权限）
      const user2 = createFakeUser({ id: 'user-002', username: 'user2' });
      const token2 = createFakeToken(['perm.b', 'perm.c']);
      mockApiLogin.mockResolvedValue(createFakeLoginResponse({ user: user2, access_token: token2 }));
      await useAuthStore.getState().login('user2', 'pass2');

      expect(useAuthStore.getState().user?.id).toBe('user-002');
      expect(useAuthStore.getState().permissions).toEqual(['perm.b', 'perm.c']);
    });

    it('登出后的状态应与初始状态一致', async () => {
      // 登录
      mockApiLogin.mockResolvedValue(createFakeLoginResponse());
      await useAuthStore.getState().login('user', 'pass');

      // 登出
      mockApiLogout.mockResolvedValue(undefined);
      await useAuthStore.getState().logout();

      const state = useAuthStore.getState();
      expect(state.user).toBeNull();
      expect(state.isAuthenticated).toBe(false);
      expect(state.loading).toBe(false);
      expect(state.permissions).toEqual([]);
    });
  });

  // =========================================================================
  // 边界情况
  // =========================================================================
  describe('边界情况', () => {
    it('并发登录不应导致状态不一致', async () => {
      const user1 = createFakeUser({ id: 'user-001' });
      const user2 = createFakeUser({ id: 'user-002' });

      let resolveLogin1: (value: unknown) => void;
      let resolveLogin2: (value: unknown) => void;

      mockApiLogin.mockImplementationOnce(() => new Promise((r) => { resolveLogin1 = r; }));
      mockApiLogin.mockImplementationOnce(() => new Promise((r) => { resolveLogin2 = r; }));

      const login1 = useAuthStore.getState().login('user1', 'pass1');
      const login2 = useAuthStore.getState().login('user2', 'pass2');

      // 第二次登录先完成
      resolveLogin2!(createFakeLoginResponse({ user: user2 }));
      await login2;
      expect(useAuthStore.getState().user?.id).toBe('user-002');

      // 第一次登录后完成（覆盖第二次）
      resolveLogin1!(createFakeLoginResponse({ user: user1 }));
      await login1;
      expect(useAuthStore.getState().user?.id).toBe('user-001');

      // 最终 loading 为 false
      expect(useAuthStore.getState().loading).toBe(false);
    });

    it('JWT payload 含 permissions 非数组时应返回空权限', async () => {
      const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
      const payload = btoa(JSON.stringify({ permissions: 'not-an-array' }))
        .replace(/\+/g, '-')
        .replace(/\//g, '_');
      const signature = btoa('sig');
      const token = `${header}.${payload}.${signature}`;

      mockApiLogin.mockResolvedValue(createFakeLoginResponse({ access_token: token }));
      await useAuthStore.getState().login('user', 'pass');

      expect(useAuthStore.getState().permissions).toEqual([]);
    });
  });
});
