/* eslint-disable no-console */

import { getStorage } from './db';

// Token 配置
export const TOKEN_CONFIG = {
  ACCESS_TOKEN_AGE: 4 * 60 * 60 * 1000,           // 4 小时
  REFRESH_TOKEN_AGE: 60 * 24 * 60 * 60 * 1000,    // 60 天
  RENEWAL_THRESHOLD: 10 * 60 * 1000,              // 剩余 10 分钟时自动续期
};

interface TokenData {
  token: string;
  deviceInfo: string;
  createdAt: number;
  expiresAt: number;
  lastUsed: number;
}

// 生成随机 Token ID
export function generateTokenId(): string {
  const array = new Uint8Array(16);
  crypto.getRandomValues(array);
  return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}

// 生成随机 Refresh Token
export function generateRefreshToken(): string {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}

// 存储 Refresh Token（使用 Redis Hash）
export async function storeRefreshToken(
  username: string,
  tokenId: string,
  tokenData: TokenData
): Promise<void> {
  const hashKey = `user_tokens:${username}`;
  const storage = getStorage();

  if (!storage || typeof (storage as any).adapter?.hSet !== 'function') {
    console.warn('Redis Hash not supported, skipping token storage');
    return;
  }

  try {
    await (storage as any).adapter.hSet(
      hashKey,
      tokenId,
      JSON.stringify(tokenData)
    );
    console.log(`Stored refresh token for ${username}:${tokenId}`);
  } catch (error) {
    console.error('Failed to store refresh token:', error);
    throw error;
  }
}

// 验证 Refresh Token
export async function verifyRefreshToken(
  username: string,
  tokenId: string,
  refreshToken: string
): Promise<boolean> {
  const hashKey = `user_tokens:${username}`;
  const storage = getStorage();

  if (!storage || typeof (storage as any).adapter?.hGet !== 'function') {
    console.warn('Redis Hash not supported');
    return false;
  }

  try {
    const dataStr = await (storage as any).adapter.hGet(hashKey, tokenId);

    if (!dataStr) {
      return false;
    }

    const tokenData: TokenData = JSON.parse(dataStr);

    // 检查是否过期
    if (Date.now() > tokenData.expiresAt) {
      // 过期了，删除
      await (storage as any).adapter.hDel(hashKey, tokenId);
      return false;
    }

    // 验证 Token
    if (tokenData.token !== refreshToken) {
      return false;
    }

    // 更新最后使用时间
    tokenData.lastUsed = Date.now();
    await (storage as any).adapter.hSet(
      hashKey,
      tokenId,
      JSON.stringify(tokenData)
    );

    return true;
  } catch (error) {
    console.error('Failed to verify refresh token:', error);
    return false;
  }
}

// 撤销单个 Token
export async function revokeRefreshToken(
  username: string,
  tokenId: string
): Promise<void> {
  const hashKey = `user_tokens:${username}`;
  const storage = getStorage();

  if (!storage || typeof (storage as any).adapter?.hDel !== 'function') {
    console.warn('Redis Hash not supported');
    return;
  }

  try {
    await (storage as any).adapter.hDel(hashKey, tokenId);
    console.log(`Revoked refresh token for ${username}:${tokenId}`);
  } catch (error) {
    console.error('Failed to revoke refresh token:', error);
  }
}

// 获取用户的所有设备
export async function getUserDevices(username: string): Promise<Array<{
  tokenId: string;
  deviceInfo: string;
  createdAt: number;
  lastUsed: number;
  expiresAt: number;
}>> {
  const hashKey = `user_tokens:${username}`;
  const storage = getStorage();

  if (!storage || typeof (storage as any).adapter?.hGetAll !== 'function') {
    console.warn('Redis Hash not supported');
    return [];
  }

  try {
    const allTokens = await (storage as any).adapter.hGetAll(hashKey);

    if (!allTokens || typeof allTokens !== 'object') {
      return [];
    }

    const devices = [];
    const now = Date.now();

    for (const [tokenId, dataStr] of Object.entries(allTokens)) {
      try {
        const tokenData: TokenData = JSON.parse(dataStr as string);

        // 检查是否过期
        if (now > tokenData.expiresAt) {
          // 过期了，删除
          await (storage as any).adapter.hDel(hashKey, tokenId);
          continue;
        }

        devices.push({
          tokenId,
          deviceInfo: tokenData.deviceInfo,
          createdAt: tokenData.createdAt,
          lastUsed: tokenData.lastUsed,
          expiresAt: tokenData.expiresAt,
        });
      } catch (err) {
        console.error(`Failed to parse token data for ${tokenId}:`, err);
      }
    }

    return devices;
  } catch (error) {
    console.error('Failed to get user devices:', error);
    return [];
  }
}

// 撤销所有 Token
export async function revokeAllRefreshTokens(username: string): Promise<void> {
  const hashKey = `user_tokens:${username}`;
  const storage = getStorage();

  if (!storage || typeof (storage as any).adapter?.del !== 'function') {
    console.warn('Redis Hash not supported');
    return;
  }

  try {
    await (storage as any).adapter.del(hashKey);
    console.log(`Revoked all refresh tokens for ${username}`);
  } catch (error) {
    console.error('Failed to revoke all refresh tokens:', error);
  }
}

// 清理过期的 Token（定期任务）
export async function cleanupExpiredTokens(username: string): Promise<number> {
  const hashKey = `user_tokens:${username}`;
  const storage = getStorage();

  if (!storage || typeof (storage as any).adapter?.hGetAll !== 'function') {
    return 0;
  }

  try {
    const allTokens = await (storage as any).adapter.hGetAll(hashKey);

    if (!allTokens || typeof allTokens !== 'object') {
      return 0;
    }

    const now = Date.now();
    let cleanedCount = 0;

    for (const [tokenId, dataStr] of Object.entries(allTokens)) {
      try {
        const tokenData: TokenData = JSON.parse(dataStr as string);

        if (now > tokenData.expiresAt) {
          await (storage as any).adapter.hDel(hashKey, tokenId);
          cleanedCount++;
        }
      } catch (err) {
        console.error(`Failed to parse token data for ${tokenId}:`, err);
      }
    }

    if (cleanedCount > 0) {
      console.log(`Cleaned up ${cleanedCount} expired tokens for ${username}`);
    }

    return cleanedCount;
  } catch (error) {
    console.error('Failed to cleanup expired tokens:', error);
    return 0;
  }
}
