/**
 * 状态一致性测试套件
 *
 * 测试范围：
 * 1. Token 过期处理和刷新机制
 * 2. 存储切换后数据一致性
 * 3. 会话状态管理
 * 4. 本地存储状态恢复
 */

const assert = require('assert');

// 测试结果收集器
const testResults = {
  passed: 0,
  failed: 0,
  errors: []
};

// 测试辅助函数
function test(name, fn) {
  try {
    fn();
    testResults.passed++;
    console.log(`  [PASS] ${name}`);
  } catch (error) {
    testResults.failed++;
    testResults.errors.push({ name, error: error.message });
    console.log(`  [FAIL] ${name}: ${error.message}`);
  }
}

async function asyncTest(name, fn) {
  try {
    await fn();
    testResults.passed++;
    console.log(`  [PASS] ${name}`);
  } catch (error) {
    testResults.failed++;
    testResults.errors.push({ name, error: error.message });
    console.log(`  [FAIL] ${name}: ${error.message}`);
  }
}

async function runTests() {

// ============================================================
// 1. Token 管理测试
// ============================================================

console.log('\n========== 1. Token 管理测试 ==========\n');

function testTokenManagement() {
  console.log('--- 测试 Token 过期和刷新机制 ---');

  // 模拟 JWT Token 结构
  function createMockToken(payload, expiresInMs) {
    const header = { alg: 'HS256', typ: 'JWT' };
    const iat = Math.floor(Date.now() / 1000);
    const exp = iat + Math.floor(expiresInMs / 1000);
    const tokenPayload = { ...payload, iat, exp };

    // 简化的 base64 编码（仅用于测试）
    const base64Header = Buffer.from(JSON.stringify(header)).toString('base64url');
    const base64Payload = Buffer.from(JSON.stringify(tokenPayload)).toString('base64url');

    return `${base64Header}.${base64Payload}.signature`;
  }

  // 解析 Token 并检查过期
  function parseToken(token) {
    try {
      const parts = token.split('.');
      if (parts.length !== 3) return null;

      const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());
      const now = Math.floor(Date.now() / 1000);

      return {
        ...payload,
        isExpired: payload.exp < now,
        expiresIn: (payload.exp - now) * 1000
      };
    } catch {
      return null;
    }
  }

  // 检查是否需要刷新 Token（提前 5 分钟刷新）
  function needsRefresh(token, thresholdMs = 5 * 60 * 1000) {
    const parsed = parseToken(token);
    if (!parsed) return true;
    return parsed.expiresIn < thresholdMs;
  }

  test('有效 Token 应该能正确解析', () => {
    const token = createMockToken({ id: 1, username: 'test' }, 2 * 60 * 60 * 1000);
    const parsed = parseToken(token);

    assert.ok(parsed, 'Token 应该能被解析');
    assert.strictEqual(parsed.id, 1);
    assert.strictEqual(parsed.username, 'test');
    assert.strictEqual(parsed.isExpired, false);
  });

  test('过期 Token 应该被正确识别', () => {
    const token = createMockToken({ id: 1 }, -1000); // 已过期
    const parsed = parseToken(token);

    assert.ok(parsed.isExpired, 'Token 应该被标记为过期');
  });

  test('即将过期的 Token 应该触发刷新', () => {
    const token = createMockToken({ id: 1 }, 3 * 60 * 1000); // 3 分钟后过期
    assert.ok(needsRefresh(token, 5 * 60 * 1000), '3 分钟后过期的 Token 应该触发刷新');
  });

  test('有效期充足的 Token 不应该触发刷新', () => {
    const token = createMockToken({ id: 1 }, 30 * 60 * 1000); // 30 分钟后过期
    assert.ok(!needsRefresh(token, 5 * 60 * 1000), '30 分钟后过期的 Token 不应该触发刷新');
  });

  test('无效 Token 格式应该返回 null', () => {
    assert.strictEqual(parseToken('invalid'), null);
    assert.strictEqual(parseToken('a.b'), null);
    assert.strictEqual(parseToken(''), null);
  });
}

testTokenManagement();

// ============================================================
// 2. 存储切换一致性测试
// ============================================================

console.log('\n========== 2. 存储切换一致性测试 ==========\n');

function testStorageSwitchConsistency() {
  console.log('--- 测试存储类型切换数据一致性 ---');

  // 模拟用户存储状态
  class UserStorageState {
    constructor(user) {
      this.userId = user.id;
      this.storageType = user.current_storage_type || 'oss';
      this.permission = user.storage_permission || 'oss_only';
      this.localQuota = user.local_storage_quota || 1073741824;
      this.localUsed = user.local_storage_used || 0;
      this.hasOssConfig = user.has_oss_config || 0;
    }

    // 检查是否可以切换到指定存储类型
    canSwitchTo(targetType) {
      // 检查权限
      if (this.permission === 'oss_only' && targetType === 'local') {
        return { allowed: false, reason: '您没有使用本地存储的权限' };
      }
      if (this.permission === 'local_only' && targetType === 'oss') {
        return { allowed: false, reason: '您没有使用 OSS 存储的权限' };
      }

      // 检查 OSS 配置
      if (targetType === 'oss' && !this.hasOssConfig) {
        return { allowed: false, reason: '请先配置 OSS 服务' };
      }

      // 检查本地存储配额
      if (targetType === 'local' && this.localUsed >= this.localQuota) {
        return { allowed: false, reason: '本地存储空间已满' };
      }

      return { allowed: true };
    }

    // 切换存储类型
    switchTo(targetType) {
      const check = this.canSwitchTo(targetType);
      if (!check.allowed) {
        throw new Error(check.reason);
      }
      this.storageType = targetType;
      return true;
    }

    // 获取当前可用空间
    getAvailableSpace() {
      if (this.storageType === 'local') {
        return this.localQuota - this.localUsed;
      }
      return null; // OSS 空间由用户 Bucket 决定
    }
  }

  test('OSS only 权限用户不能切换到本地存储', () => {
    const user = { id: 1, storage_permission: 'oss_only', has_oss_config: 1 };
    const state = new UserStorageState(user);

    const result = state.canSwitchTo('local');
    assert.strictEqual(result.allowed, false);
    assert.ok(result.reason.includes('权限'));
  });

  test('本地 only 权限用户不能切换到 OSS 存储', () => {
    const user = { id: 1, storage_permission: 'local_only' };
    const state = new UserStorageState(user);

    const result = state.canSwitchTo('oss');
    assert.strictEqual(result.allowed, false);
    assert.ok(result.reason.includes('权限'));
  });

  test('未配置 OSS 的用户不能切换到 OSS', () => {
    const user = { id: 1, storage_permission: 'both', has_oss_config: 0 };
    const state = new UserStorageState(user);

    const result = state.canSwitchTo('oss');
    assert.strictEqual(result.allowed, false);
    assert.ok(result.reason.includes('配置'));
  });

  test('本地存储已满时不能切换到本地', () => {
    const user = {
      id: 1,
      storage_permission: 'both',
      local_storage_quota: 1000,
      local_storage_used: 1000
    };
    const state = new UserStorageState(user);

    const result = state.canSwitchTo('local');
    assert.strictEqual(result.allowed, false);
    assert.ok(result.reason.includes('已满'));
  });

  test('有权限且已配置的用户可以自由切换', () => {
    const user = {
      id: 1,
      storage_permission: 'both',
      has_oss_config: 1,
      local_storage_quota: 10000,
      local_storage_used: 5000
    };
    const state = new UserStorageState(user);

    assert.ok(state.canSwitchTo('oss').allowed);
    assert.ok(state.canSwitchTo('local').allowed);
  });

  test('切换后状态应该正确更新', () => {
    const user = {
      id: 1,
      storage_permission: 'both',
      has_oss_config: 1,
      current_storage_type: 'oss'
    };
    const state = new UserStorageState(user);

    assert.strictEqual(state.storageType, 'oss');
    state.switchTo('local');
    assert.strictEqual(state.storageType, 'local');
  });
}

testStorageSwitchConsistency();

// ============================================================
// 3. 会话状态管理测试
// ============================================================

console.log('\n========== 3. 会话状态管理测试 ==========\n');

async function testSessionManagement() {
  console.log('--- 测试会话状态管理 ---');

  // 模拟会话管理器
  class SessionManager {
    constructor() {
      this.sessions = new Map();
      this.sessionTTL = 30 * 60 * 1000; // 30 分钟
    }

    createSession(userId) {
      const sessionId = `sess_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
      const session = {
        id: sessionId,
        userId,
        createdAt: Date.now(),
        lastActivity: Date.now(),
        data: {}
      };
      this.sessions.set(sessionId, session);
      return sessionId;
    }

    getSession(sessionId) {
      const session = this.sessions.get(sessionId);
      if (!session) return null;

      // 检查会话是否过期
      if (Date.now() - session.lastActivity > this.sessionTTL) {
        this.sessions.delete(sessionId);
        return null;
      }

      // 更新最后活动时间
      session.lastActivity = Date.now();
      return session;
    }

    updateSessionData(sessionId, data) {
      const session = this.getSession(sessionId);
      if (!session) return false;

      session.data = { ...session.data, ...data };
      return true;
    }

    destroySession(sessionId) {
      return this.sessions.delete(sessionId);
    }

    getActiveSessions(userId) {
      const now = Date.now();
      const active = [];
      for (const session of this.sessions.values()) {
        if (session.userId === userId && now - session.lastActivity <= this.sessionTTL) {
          active.push(session);
        }
      }
      return active;
    }

    // 强制登出用户所有会话
    destroyUserSessions(userId) {
      let count = 0;
      for (const [sessionId, session] of this.sessions.entries()) {
        if (session.userId === userId) {
          this.sessions.delete(sessionId);
          count++;
        }
      }
      return count;
    }
  }

  const manager = new SessionManager();

  await asyncTest('创建会话应该返回有效的会话 ID', async () => {
    const sessionId = manager.createSession(1);
    assert.ok(sessionId.startsWith('sess_'));
    assert.ok(manager.getSession(sessionId) !== null);
  });

  await asyncTest('获取会话应该返回正确的用户 ID', async () => {
    const sessionId = manager.createSession(42);
    const session = manager.getSession(sessionId);
    assert.strictEqual(session.userId, 42);
  });

  await asyncTest('更新会话数据应该持久化', async () => {
    const sessionId = manager.createSession(1);
    manager.updateSessionData(sessionId, { captcha: 'ABC123' });

    const session = manager.getSession(sessionId);
    assert.strictEqual(session.data.captcha, 'ABC123');
  });

  await asyncTest('销毁会话后应该无法获取', async () => {
    const sessionId = manager.createSession(1);
    manager.destroySession(sessionId);
    assert.strictEqual(manager.getSession(sessionId), null);
  });

  await asyncTest('过期会话应该被自动清理', async () => {
    const shortTTLManager = new SessionManager();
    shortTTLManager.sessionTTL = 10; // 10ms

    const sessionId = shortTTLManager.createSession(1);
    await new Promise(resolve => setTimeout(resolve, 20));

    assert.strictEqual(shortTTLManager.getSession(sessionId), null);
  });

  await asyncTest('强制登出应该清除用户所有会话', async () => {
    const sessionId1 = manager.createSession(100);
    const sessionId2 = manager.createSession(100);
    const sessionId3 = manager.createSession(100);

    const count = manager.destroyUserSessions(100);
    assert.strictEqual(count, 3);
    assert.strictEqual(manager.getSession(sessionId1), null);
    assert.strictEqual(manager.getSession(sessionId2), null);
    assert.strictEqual(manager.getSession(sessionId3), null);
  });
}

await testSessionManagement();

// ============================================================
// 4. 本地存储状态恢复测试
// ============================================================

console.log('\n========== 4. 本地存储状态恢复测试 ==========\n');

function testLocalStorageRecovery() {
  console.log('--- 测试本地存储状态恢复 ---');

  // 模拟 localStorage
  class MockLocalStorage {
    constructor() {
      this.store = {};
    }

    getItem(key) {
      return this.store[key] || null;
    }

    setItem(key, value) {
      this.store[key] = String(value);
    }

    removeItem(key) {
      delete this.store[key];
    }

    clear() {
      this.store = {};
    }
  }

  // 状态恢复管理器
  class StateRecoveryManager {
    constructor(storage) {
      this.storage = storage;
      this.stateKey = 'app_state';
    }

    // 保存状态
    saveState(state) {
      try {
        const serialized = JSON.stringify({
          ...state,
          savedAt: Date.now()
        });
        this.storage.setItem(this.stateKey, serialized);
        return true;
      } catch (e) {
        console.error('保存状态失败:', e);
        return false;
      }
    }

    // 恢复状态
    restoreState(maxAgeMs = 24 * 60 * 60 * 1000) {
      try {
        const serialized = this.storage.getItem(this.stateKey);
        if (!serialized) return null;

        const state = JSON.parse(serialized);

        // 检查状态是否过期
        if (Date.now() - state.savedAt > maxAgeMs) {
          this.clearState();
          return null;
        }

        // 移除元数据
        delete state.savedAt;
        return state;
      } catch (e) {
        console.error('恢复状态失败:', e);
        return null;
      }
    }

    // 清除状态
    clearState() {
      this.storage.removeItem(this.stateKey);
    }

    // 合并恢复的状态和默认状态
    mergeWithDefaults(defaults) {
      const restored = this.restoreState();
      if (!restored) return defaults;

      // 只恢复允许持久化的字段
      const allowedFields = ['currentView', 'fileViewMode', 'adminTab', 'currentPath'];
      const merged = { ...defaults };

      for (const field of allowedFields) {
        if (field in restored) {
          merged[field] = restored[field];
        }
      }

      return merged;
    }
  }

  const storage = new MockLocalStorage();
  const manager = new StateRecoveryManager(storage);

  test('保存和恢复状态应该正常工作', () => {
    const state = { currentView: 'files', currentPath: '/documents' };
    manager.saveState(state);

    const restored = manager.restoreState();
    assert.strictEqual(restored.currentView, 'files');
    assert.strictEqual(restored.currentPath, '/documents');
  });

  test('空存储应该返回 null', () => {
    const emptyStorage = new MockLocalStorage();
    const emptyManager = new StateRecoveryManager(emptyStorage);
    assert.strictEqual(emptyManager.restoreState(), null);
  });

  test('过期状态应该被清除', () => {
    // 手动设置一个过期的状态
    storage.setItem('app_state', JSON.stringify({
      currentView: 'old',
      savedAt: Date.now() - 48 * 60 * 60 * 1000 // 48小时前
    }));

    const restored = manager.restoreState(24 * 60 * 60 * 1000);
    assert.strictEqual(restored, null);
  });

  test('清除状态后应该无法恢复', () => {
    manager.saveState({ test: 'value' });
    manager.clearState();
    assert.strictEqual(manager.restoreState(), null);
  });

  test('合并默认值应该优先使用恢复的值', () => {
    manager.saveState({ currentView: 'shares', adminTab: 'users' });

    const defaults = { currentView: 'files', fileViewMode: 'grid', adminTab: 'overview' };
    const merged = manager.mergeWithDefaults(defaults);

    assert.strictEqual(merged.currentView, 'shares');
    assert.strictEqual(merged.adminTab, 'users');
    assert.strictEqual(merged.fileViewMode, 'grid'); // 默认值
  });

  test('损坏的 JSON 应该返回 null', () => {
    storage.setItem('app_state', 'not valid json{');
    assert.strictEqual(manager.restoreState(), null);
  });
}

testLocalStorageRecovery();

// ============================================================
// 5. 并发状态更新测试
// ============================================================

console.log('\n========== 5. 并发状态更新测试 ==========\n');

async function testConcurrentStateUpdates() {
  console.log('--- 测试并发状态更新 ---');

  // 简单的状态管理器（带版本控制）
  class VersionedStateManager {
    constructor(initialState = {}) {
      this.state = { ...initialState };
      this.version = 0;
      this.updateQueue = [];
      this.processing = false;
    }

    getState() {
      return { ...this.state };
    }

    getVersion() {
      return this.version;
    }

    // 乐观锁更新
    async updateWithVersion(expectedVersion, updates) {
      return new Promise((resolve, reject) => {
        this.updateQueue.push({
          expectedVersion,
          updates,
          resolve,
          reject
        });
        this.processQueue();
      });
    }

    // 强制更新（忽略版本）
    forceUpdate(updates) {
      this.state = { ...this.state, ...updates };
      this.version++;
      return { success: true, version: this.version };
    }

    async processQueue() {
      if (this.processing || this.updateQueue.length === 0) return;

      this.processing = true;

      while (this.updateQueue.length > 0) {
        const { expectedVersion, updates, resolve, reject } = this.updateQueue.shift();

        if (expectedVersion !== this.version) {
          reject(new Error('版本冲突，请刷新后重试'));
          continue;
        }

        this.state = { ...this.state, ...updates };
        this.version++;
        resolve({ success: true, version: this.version, state: this.getState() });
      }

      this.processing = false;
    }
  }

  await asyncTest('顺序更新应该成功', async () => {
    const manager = new VersionedStateManager({ count: 0 });

    await manager.updateWithVersion(0, { count: 1 });
    await manager.updateWithVersion(1, { count: 2 });

    assert.strictEqual(manager.getState().count, 2);
    assert.strictEqual(manager.getVersion(), 2);
  });

  await asyncTest('版本冲突应该被检测', async () => {
    const manager = new VersionedStateManager({ count: 0 });

    // 第一个更新成功
    await manager.updateWithVersion(0, { count: 1 });

    // 使用旧版本尝试更新应该失败
    try {
      await manager.updateWithVersion(0, { count: 2 });
      assert.fail('应该抛出版本冲突错误');
    } catch (error) {
      assert.ok(error.message.includes('冲突'));
    }
  });

  await asyncTest('强制更新应该忽略版本', async () => {
    const manager = new VersionedStateManager({ value: 'old' });

    manager.forceUpdate({ value: 'new' });
    assert.strictEqual(manager.getState().value, 'new');
  });

  await asyncTest('并发更新应该按顺序处理', async () => {
    const manager = new VersionedStateManager({ count: 0 });

    // 模拟并发更新
    const results = await Promise.allSettled([
      manager.updateWithVersion(0, { count: 1 }),
      manager.updateWithVersion(0, { count: 2 }), // 这个会失败
      manager.updateWithVersion(0, { count: 3 })  // 这个也会失败
    ]);

    const fulfilled = results.filter(r => r.status === 'fulfilled').length;
    const rejected = results.filter(r => r.status === 'rejected').length;

    assert.strictEqual(fulfilled, 1, '应该只有一个更新成功');
    assert.strictEqual(rejected, 2, '应该有两个更新失败');
  });
}

await testConcurrentStateUpdates();

// ============================================================
// 6. 视图切换状态测试
// ============================================================

console.log('\n========== 6. 视图切换状态测试 ==========\n');

function testViewSwitchState() {
  console.log('--- 测试视图切换状态保持 ---');

  // 视图状态管理器
  class ViewStateManager {
    constructor() {
      this.currentView = 'files';
      this.viewStates = {
        files: { path: '/', viewMode: 'grid', selection: [] },
        shares: { viewMode: 'list', filter: 'all' },
        admin: { tab: 'overview' }
      };
    }

    switchTo(view) {
      if (!this.viewStates[view]) {
        throw new Error(`未知视图: ${view}`);
      }
      this.currentView = view;
      return this.getViewState(view);
    }

    getViewState(view) {
      return { ...this.viewStates[view || this.currentView] };
    }

    updateViewState(view, updates) {
      if (!this.viewStates[view]) {
        throw new Error(`未知视图: ${view}`);
      }
      this.viewStates[view] = { ...this.viewStates[view], ...updates };
    }

    // 获取完整状态快照
    getSnapshot() {
      return {
        currentView: this.currentView,
        viewStates: JSON.parse(JSON.stringify(this.viewStates))
      };
    }

    // 从快照恢复
    restoreFromSnapshot(snapshot) {
      this.currentView = snapshot.currentView;
      this.viewStates = JSON.parse(JSON.stringify(snapshot.viewStates));
    }
  }

  const manager = new ViewStateManager();

  test('切换视图应该返回该视图的状态', () => {
    const state = manager.switchTo('shares');
    assert.strictEqual(state.viewMode, 'list');
    assert.strictEqual(state.filter, 'all');
  });

  test('更新视图状态应该被保存', () => {
    manager.updateViewState('files', { path: '/documents', selection: ['file1.txt'] });
    const state = manager.getViewState('files');
    assert.strictEqual(state.path, '/documents');
    assert.strictEqual(state.selection.length, 1);
  });

  test('切换视图后再切换回来应该保留状态', () => {
    manager.updateViewState('files', { path: '/photos' });
    manager.switchTo('shares');
    manager.switchTo('files');

    const state = manager.getViewState('files');
    assert.strictEqual(state.path, '/photos');
  });

  test('切换到未知视图应该抛出错误', () => {
    assert.throws(() => manager.switchTo('unknown'), /未知视图/);
  });

  test('快照和恢复应该正常工作', () => {
    manager.updateViewState('files', { path: '/backup' });
    const snapshot = manager.getSnapshot();

    // 修改状态
    manager.updateViewState('files', { path: '/different' });

    // 从快照恢复
    manager.restoreFromSnapshot(snapshot);

    const state = manager.getViewState('files');
    assert.strictEqual(state.path, '/backup');
  });
}

testViewSwitchState();

// ============================================================
// 7. 主题切换一致性测试
// ============================================================

console.log('\n========== 7. 主题切换一致性测试 ==========\n');

function testThemeConsistency() {
  console.log('--- 测试主题切换一致性 ---');

  // 主题管理器
  class ThemeManager {
    constructor(globalDefault = 'dark') {
      this.globalTheme = globalDefault;
      this.userTheme = null; // null 表示跟随全局
    }

    setGlobalTheme(theme) {
      if (!['dark', 'light'].includes(theme)) {
        throw new Error('无效的主题');
      }
      this.globalTheme = theme;
    }

    setUserTheme(theme) {
      if (theme !== null && !['dark', 'light'].includes(theme)) {
        throw new Error('无效的主题');
      }
      this.userTheme = theme;
    }

    getEffectiveTheme() {
      return this.userTheme || this.globalTheme;
    }

    isFollowingGlobal() {
      return this.userTheme === null;
    }

    getThemeInfo() {
      return {
        global: this.globalTheme,
        user: this.userTheme,
        effective: this.getEffectiveTheme(),
        followingGlobal: this.isFollowingGlobal()
      };
    }
  }

  test('默认应该使用全局主题', () => {
    const manager = new ThemeManager('dark');
    assert.strictEqual(manager.getEffectiveTheme(), 'dark');
    assert.ok(manager.isFollowingGlobal());
  });

  test('用户主题应该覆盖全局主题', () => {
    const manager = new ThemeManager('dark');
    manager.setUserTheme('light');

    assert.strictEqual(manager.getEffectiveTheme(), 'light');
    assert.ok(!manager.isFollowingGlobal());
  });

  test('用户主题设为 null 应该跟随全局', () => {
    const manager = new ThemeManager('dark');
    manager.setUserTheme('light');
    manager.setUserTheme(null);

    assert.strictEqual(manager.getEffectiveTheme(), 'dark');
    assert.ok(manager.isFollowingGlobal());
  });

  test('全局主题改变应该影响跟随全局的用户', () => {
    const manager = new ThemeManager('dark');

    manager.setGlobalTheme('light');
    assert.strictEqual(manager.getEffectiveTheme(), 'light');
  });

  test('全局主题改变不应该影响有自定义主题的用户', () => {
    const manager = new ThemeManager('dark');
    manager.setUserTheme('light');

    manager.setGlobalTheme('dark');
    assert.strictEqual(manager.getEffectiveTheme(), 'light');
  });

  test('无效主题应该抛出错误', () => {
    const manager = new ThemeManager();
    assert.throws(() => manager.setGlobalTheme('invalid'), /无效/);
    assert.throws(() => manager.setUserTheme('invalid'), /无效/);
  });
}

testThemeConsistency();

// ============================================================
// 测试总结
// ============================================================

console.log('\n========================================');
console.log('测试总结');
console.log('========================================');
console.log(`通过: ${testResults.passed}`);
console.log(`失败: ${testResults.failed}`);

if (testResults.errors.length > 0) {
  console.log('\n失败的测试:');
  testResults.errors.forEach((e, i) => {
    console.log(`  ${i + 1}. ${e.name}: ${e.error}`);
  });
}

console.log('\n');

return testResults;
}

// 运行测试
runTests().then(testResults => {
  process.exit(testResults.failed > 0 ? 1 : 0);
}).catch(err => {
  console.error('测试执行错误:', err);
  process.exit(1);
});
