/**
 * 验证码存储服务
 * 支持两种模式：
 * 1. Redis（生产环境，REDIS_URL 环境变量存在时自动启用）
 * 2. 内存 Map（开发环境 fallback）
 * 
 * 功能：存储、验证、过期清理、失败次数限制
 */

import logger from './logger'

interface CodeEntry {
  code: string
  expiresAt: number
  failCount: number
  lastSentAt: number
}

// 内存存储 fallback
const memoryStore = new Map<string, CodeEntry>()

// 是否启用 Redis
const REDIS_URL = process.env.REDIS_URL || ''
const useRedis = !!REDIS_URL

// Redis 客户端（懒加载）
let redisClient: any = null
async function getRedis() {
  if (!redisClient && useRedis) {
    try {
      const { createClient } = await import('redis')
      redisClient = createClient({ url: REDIS_URL })
      redisClient.on('error', (err: any) => logger.error({ err }, 'Redis error'))
      await redisClient.connect()
      logger.info('Redis 验证码存储已连接')
    } catch (err) {
      logger.warn({ err }, 'Redis 连接失败，降级为内存存储')
      return null
    }
  }
  return redisClient
}

const KEY_PREFIX = 'vcode:'
const TTL_SECONDS = 300 // 5 分钟

/**
 * 存储验证码
 */
export async function setCode(key: string, code: string, ttlMs: number = 5 * 60 * 1000): Promise<void> {
  const entry: CodeEntry = {
    code,
    expiresAt: Date.now() + ttlMs,
    failCount: 0,
    lastSentAt: Date.now(),
  }

  const redis = await getRedis()
  if (redis) {
    try {
      await redis.set(`${KEY_PREFIX}${key}`, JSON.stringify(entry), { PX: ttlMs })
      return
    } catch (err) {
      logger.warn({ err }, 'Redis set 失败，降级内存')
    }
  }
  memoryStore.set(key, entry)
}

/**
 * 获取验证码条目
 */
export async function getCode(key: string): Promise<CodeEntry | null> {
  const redis = await getRedis()
  if (redis) {
    try {
      const raw = await redis.get(`${KEY_PREFIX}${key}`)
      if (!raw) return null
      const entry = JSON.parse(raw) as CodeEntry
      if (entry.expiresAt < Date.now()) {
        await redis.del(`${KEY_PREFIX}${key}`)
        return null
      }
      return entry
    } catch (err) {
      logger.warn({ err }, 'Redis get 失败，降级内存')
    }
  }

  const entry = memoryStore.get(key)
  if (!entry) return null
  if (entry.expiresAt < Date.now()) {
    memoryStore.delete(key)
    return null
  }
  return entry
}

/**
 * 更新验证码条目（如递增失败次数）
 */
export async function updateCode(key: string, updates: Partial<CodeEntry>): Promise<void> {
  const redis = await getRedis()
  if (redis) {
    try {
      const raw = await redis.get(`${KEY_PREFIX}${key}`)
      if (raw) {
        const entry = { ...JSON.parse(raw), ...updates }
        await redis.set(`${KEY_PREFIX}${key}`, JSON.stringify(entry), { PX: TTL_SECONDS * 1000 })
      }
      return
    } catch (err) {
      logger.warn({ err }, 'Redis update 失败，降级内存')
    }
  }

  const entry = memoryStore.get(key)
  if (entry) {
    Object.assign(entry, updates)
  }
}

/**
 * 删除验证码
 */
export async function deleteCode(key: string): Promise<void> {
  const redis = await getRedis()
  if (redis) {
    try {
      await redis.del(`${KEY_PREFIX}${key}`)
      return
    } catch (err) {
      logger.warn({ err }, 'Redis del 失败，降级内存')
    }
  }
  memoryStore.delete(key)
}

/**
 * 检查发送频率限制（60秒内不可重复发送）
 */
export async function checkRateLimit(key: string, intervalMs: number = 60 * 1000): Promise<boolean> {
  const entry = await getCode(key)
  if (entry && entry.lastSentAt && Date.now() - entry.lastSentAt < intervalMs) {
    return false // 被限流
  }
  return true // 允许发送
}
