#!/usr/bin/env node

/**
 * 从日志文件分析Claude账户请求时间的CLI工具
 * 用于恢复会话窗口数据
 */

const fs = require('fs')
const path = require('path')
const readline = require('readline')
const zlib = require('zlib')
const redis = require('../src/models/redis')

class LogSessionAnalyzer {
  constructor() {
    // 更新正则表达式以匹配实际的日志格式
    this.accountUsagePattern =
      /🎯 Using sticky session shared account: (.+?) \(([a-f0-9-]{36})\) for session ([a-f0-9]+)/
    this.processingPattern =
      /📡 Processing streaming API request with usage capture for key: (.+?), account: ([a-f0-9-]{36}), session: ([a-f0-9]+)/
    this.completedPattern = /🔗 ✅ Request completed in (\d+)ms for key: (.+)/
    this.usageRecordedPattern =
      /🔗 📊 Stream usage recorded \(real\) - Model: (.+?), Input: (\d+), Output: (\d+), Cache Create: (\d+), Cache Read: (\d+), Total: (\d+) tokens/
    this.timestampPattern = /\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]/
    this.accounts = new Map()
    this.requestHistory = []
    this.sessions = new Map() // 记录会话信息
  }

  // 解析时间戳
  parseTimestamp(line) {
    const match = line.match(this.timestampPattern)
    if (match) {
      return new Date(match[1])
    }
    return null
  }

  // 分析单个日志文件
  async analyzeLogFile(filePath) {
    console.log(`📖 分析日志文件: ${filePath}`)

    let fileStream = fs.createReadStream(filePath)

    // 如果是gz文件，需要先解压
    if (filePath.endsWith('.gz')) {
      console.log('   🗜️  检测到gz压缩文件，正在解压...')
      fileStream = fileStream.pipe(zlib.createGunzip())
    }

    const rl = readline.createInterface({
      input: fileStream,
      crlfDelay: Infinity
    })

    let lineCount = 0
    let requestCount = 0
    let usageCount = 0

    for await (const line of rl) {
      lineCount++

      // 解析时间戳
      const timestamp = this.parseTimestamp(line)
      if (!timestamp) {
        continue
      }

      // 查找账户使用记录
      const accountUsageMatch = line.match(this.accountUsagePattern)
      if (accountUsageMatch) {
        const accountName = accountUsageMatch[1]
        const accountId = accountUsageMatch[2]
        const sessionId = accountUsageMatch[3]

        if (!this.accounts.has(accountId)) {
          this.accounts.set(accountId, {
            accountId,
            accountName,
            requests: [],
            firstRequest: timestamp,
            lastRequest: timestamp,
            totalRequests: 0,
            sessions: new Set()
          })
        }

        const account = this.accounts.get(accountId)
        account.sessions.add(sessionId)

        if (timestamp < account.firstRequest) {
          account.firstRequest = timestamp
        }
        if (timestamp > account.lastRequest) {
          account.lastRequest = timestamp
        }
      }

      // 查找请求处理记录
      const processingMatch = line.match(this.processingPattern)
      if (processingMatch) {
        const apiKeyName = processingMatch[1]
        const accountId = processingMatch[2]
        const sessionId = processingMatch[3]

        if (!this.accounts.has(accountId)) {
          this.accounts.set(accountId, {
            accountId,
            accountName: 'Unknown',
            requests: [],
            firstRequest: timestamp,
            lastRequest: timestamp,
            totalRequests: 0,
            sessions: new Set()
          })
        }

        const account = this.accounts.get(accountId)
        account.requests.push({
          timestamp,
          apiKeyName,
          sessionId,
          type: 'processing'
        })

        account.sessions.add(sessionId)
        account.totalRequests++
        requestCount++

        if (timestamp > account.lastRequest) {
          account.lastRequest = timestamp
        }

        // 记录到全局请求历史
        this.requestHistory.push({
          timestamp,
          accountId,
          apiKeyName,
          sessionId,
          type: 'processing'
        })
      }

      // 查找请求完成记录
      const completedMatch = line.match(this.completedPattern)
      if (completedMatch) {
        const duration = parseInt(completedMatch[1])
        const apiKeyName = completedMatch[2]

        // 记录到全局请求历史
        this.requestHistory.push({
          timestamp,
          apiKeyName,
          duration,
          type: 'completed'
        })
      }

      // 查找使用统计记录
      const usageMatch = line.match(this.usageRecordedPattern)
      if (usageMatch) {
        const model = usageMatch[1]
        const inputTokens = parseInt(usageMatch[2])
        const outputTokens = parseInt(usageMatch[3])
        const cacheCreateTokens = parseInt(usageMatch[4])
        const cacheReadTokens = parseInt(usageMatch[5])
        const totalTokens = parseInt(usageMatch[6])

        usageCount++

        // 记录到全局请求历史
        this.requestHistory.push({
          timestamp,
          type: 'usage',
          model,
          inputTokens,
          outputTokens,
          cacheCreateTokens,
          cacheReadTokens,
          totalTokens
        })
      }
    }

    console.log(
      `   📊 解析完成: ${lineCount} 行, 找到 ${requestCount} 个请求记录, ${usageCount} 个使用统计`
    )
  }

  // 分析日志目录中的所有文件
  async analyzeLogDirectory(logDir = './logs') {
    console.log(`🔍 扫描日志目录: ${logDir}\n`)

    try {
      const files = fs.readdirSync(logDir)
      const logFiles = files
        .filter(
          (file) =>
            file.includes('claude-relay') &&
            (file.endsWith('.log') ||
              file.endsWith('.log.1') ||
              file.endsWith('.log.gz') ||
              file.match(/\.log\.\d+\.gz$/) ||
              file.match(/\.log\.\d+$/))
        )
        .sort()
        .reverse() // 最新的文件优先

      if (logFiles.length === 0) {
        console.log('❌ 没有找到日志文件')
        return
      }

      console.log(`📁 找到 ${logFiles.length} 个日志文件:`)
      logFiles.forEach((file) => console.log(`   - ${file}`))
      console.log('')

      // 分析每个文件
      for (const file of logFiles) {
        const filePath = path.join(logDir, file)
        await this.analyzeLogFile(filePath)
      }
    } catch (error) {
      console.error(`❌ 读取日志目录失败: ${error.message}`)
      throw error
    }
  }

  // 分析单个日志文件（支持直接传入文件路径）
  async analyzeSingleFile(filePath) {
    console.log(`🔍 分析单个日志文件: ${filePath}\n`)

    try {
      if (!fs.existsSync(filePath)) {
        console.log('❌ 文件不存在')
        return
      }

      await this.analyzeLogFile(filePath)
    } catch (error) {
      console.error(`❌ 分析文件失败: ${error.message}`)
      throw error
    }
  }

  // 计算会话窗口
  calculateSessionWindow(requestTime) {
    const hour = requestTime.getHours()
    const windowStartHour = Math.floor(hour / 5) * 5

    const windowStart = new Date(requestTime)
    windowStart.setHours(windowStartHour, 0, 0, 0)

    const windowEnd = new Date(windowStart)
    windowEnd.setHours(windowEnd.getHours() + 5)

    return { windowStart, windowEnd }
  }

  // 分析会话窗口
  analyzeSessionWindows() {
    console.log('🕐 分析会话窗口...\n')

    const now = new Date()
    const results = []

    for (const [accountId, accountData] of this.accounts) {
      const requests = accountData.requests.sort((a, b) => a.timestamp - b.timestamp)

      // 按会话窗口分组请求
      const windowGroups = new Map()

      for (const request of requests) {
        const { windowStart, windowEnd } = this.calculateSessionWindow(request.timestamp)
        const windowKey = `${windowStart.getTime()}-${windowEnd.getTime()}`

        if (!windowGroups.has(windowKey)) {
          windowGroups.set(windowKey, {
            windowStart,
            windowEnd,
            requests: [],
            isActive: now >= windowStart && now < windowEnd
          })
        }

        windowGroups.get(windowKey).requests.push(request)
      }

      // 转换为数组并排序
      const windowArray = Array.from(windowGroups.values()).sort(
        (a, b) => b.windowStart - a.windowStart
      ) // 最新的窗口优先

      const result = {
        accountId,
        accountName: accountData.accountName,
        totalRequests: accountData.totalRequests,
        firstRequest: accountData.firstRequest,
        lastRequest: accountData.lastRequest,
        sessions: accountData.sessions,
        windows: windowArray,
        currentActiveWindow: windowArray.find((w) => w.isActive) || null,
        mostRecentWindow: windowArray[0] || null
      }

      results.push(result)
    }

    return results.sort((a, b) => b.lastRequest - a.lastRequest)
  }

  // 显示分析结果
  displayResults(results) {
    console.log('📊 分析结果:\n')
    console.log('='.repeat(80))

    for (const result of results) {
      console.log(`🏢 账户: ${result.accountName || 'Unknown'} (${result.accountId})`)
      console.log(`   总请求数: ${result.totalRequests}`)
      console.log(`   会话数: ${result.sessions ? result.sessions.size : 0}`)
      console.log(`   首次请求: ${result.firstRequest.toLocaleString()}`)
      console.log(`   最后请求: ${result.lastRequest.toLocaleString()}`)

      if (result.currentActiveWindow) {
        console.log(
          `   ✅ 当前活跃窗口: ${result.currentActiveWindow.windowStart.toLocaleString()} - ${result.currentActiveWindow.windowEnd.toLocaleString()}`
        )
        console.log(`       窗口内请求: ${result.currentActiveWindow.requests.length} 次`)
        const progress = this.calculateWindowProgress(
          result.currentActiveWindow.windowStart,
          result.currentActiveWindow.windowEnd
        )
        console.log(`       窗口进度: ${progress}%`)
      } else if (result.mostRecentWindow) {
        const window = result.mostRecentWindow
        console.log(
          `   ⏰ 最近窗口(已过期): ${window.windowStart.toLocaleString()} - ${window.windowEnd.toLocaleString()}`
        )
        console.log(`       窗口内请求: ${window.requests.length} 次`)
        const hoursAgo = Math.round((new Date() - window.windowEnd) / (1000 * 60 * 60))
        console.log(`       过期时间: ${hoursAgo} 小时前`)
      } else {
        console.log('   ❌ 无会话窗口数据')
      }

      // 显示最近几个窗口
      if (result.windows.length > 1) {
        console.log(`   📈 历史窗口: ${result.windows.length} 个`)
        const recentWindows = result.windows.slice(0, 3)
        for (let i = 0; i < recentWindows.length; i++) {
          const window = recentWindows[i]
          const status = window.isActive ? '活跃' : '已过期'
          console.log(
            `      ${i + 1}. ${window.windowStart.toLocaleString()} - ${window.windowEnd.toLocaleString()} (${status}, ${window.requests.length}次请求)`
          )
        }
      }

      // 显示最近几个会话的API Key使用情况
      const accountData = this.accounts.get(result.accountId)
      if (accountData && accountData.requests && accountData.requests.length > 0) {
        const apiKeyStats = {}

        for (const req of accountData.requests) {
          if (!apiKeyStats[req.apiKeyName]) {
            apiKeyStats[req.apiKeyName] = 0
          }
          apiKeyStats[req.apiKeyName]++
        }

        console.log('   🔑 API Key使用统计:')
        for (const [keyName, count] of Object.entries(apiKeyStats)) {
          console.log(`      - ${keyName}: ${count} 次`)
        }
      }

      console.log('')
    }

    console.log('='.repeat(80))
    console.log(`总计: ${results.length} 个账户, ${this.requestHistory.length} 个日志记录\n`)
  }

  // 计算窗口进度百分比
  calculateWindowProgress(windowStart, windowEnd) {
    const now = new Date()
    const totalDuration = windowEnd.getTime() - windowStart.getTime()
    const elapsedTime = now.getTime() - windowStart.getTime()
    return Math.max(0, Math.min(100, Math.round((elapsedTime / totalDuration) * 100)))
  }

  // 更新Redis中的会话窗口数据
  async updateRedisSessionWindows(results, dryRun = true) {
    if (dryRun) {
      console.log('🧪 模拟模式 - 不会实际更新Redis数据\n')
    } else {
      console.log('💾 更新Redis中的会话窗口数据...\n')
      await redis.connect()
    }

    let updatedCount = 0
    let skippedCount = 0

    for (const result of results) {
      try {
        const accountData = await redis.getClaudeAccount(result.accountId)

        if (!accountData || Object.keys(accountData).length === 0) {
          console.log(`⚠️  账户 ${result.accountId} 在Redis中不存在，跳过`)
          skippedCount++
          continue
        }

        console.log(`🔄 处理账户: ${accountData.name || result.accountId}`)

        // 确定要设置的会话窗口
        let targetWindow = null

        if (result.currentActiveWindow) {
          targetWindow = result.currentActiveWindow
          console.log(
            `   ✅ 使用当前活跃窗口: ${targetWindow.windowStart.toLocaleString()} - ${targetWindow.windowEnd.toLocaleString()}`
          )
        } else if (result.mostRecentWindow) {
          const window = result.mostRecentWindow
          const now = new Date()

          // 如果最近窗口是在过去24小时内的，可以考虑恢复
          const hoursSinceWindow = (now - window.windowEnd) / (1000 * 60 * 60)

          if (hoursSinceWindow <= 24) {
            console.log(
              `   🕐 最近窗口在24小时内，但已过期: ${window.windowStart.toLocaleString()} - ${window.windowEnd.toLocaleString()}`
            )
            console.log(`   ❌ 不恢复已过期窗口（${hoursSinceWindow.toFixed(1)}小时前过期）`)
          } else {
            console.log('   ⏰ 最近窗口超过24小时前，不予恢复')
          }
        }

        if (targetWindow && !dryRun) {
          // 更新Redis中的会话窗口数据
          accountData.sessionWindowStart = targetWindow.windowStart.toISOString()
          accountData.sessionWindowEnd = targetWindow.windowEnd.toISOString()
          accountData.lastUsedAt = result.lastRequest.toISOString()
          accountData.lastRequestTime = result.lastRequest.toISOString()

          await redis.setClaudeAccount(result.accountId, accountData)
          updatedCount++

          console.log('   ✅ 已更新会话窗口数据')
        } else if (targetWindow) {
          updatedCount++
          console.log(
            `   🧪 [模拟] 将设置会话窗口: ${targetWindow.windowStart.toLocaleString()} - ${targetWindow.windowEnd.toLocaleString()}`
          )
        } else {
          skippedCount++
          console.log('   ⏭️  跳过（无有效窗口）')
        }

        console.log('')
      } catch (error) {
        console.error(`❌ 处理账户 ${result.accountId} 时出错: ${error.message}`)
        skippedCount++
      }
    }

    if (!dryRun) {
      await redis.disconnect()
    }

    console.log('📊 更新结果:')
    console.log(`   ✅ 已更新: ${updatedCount}`)
    console.log(`   ⏭️  已跳过: ${skippedCount}`)
    console.log(`   📋 总计: ${results.length}`)
  }

  // 主分析函数
  async analyze(options = {}) {
    const { logDir = './logs', singleFile = null, updateRedis = false, dryRun = true } = options

    try {
      console.log('🔍 Claude账户会话窗口分析工具\n')

      // 分析日志文件
      if (singleFile) {
        await this.analyzeSingleFile(singleFile)
      } else {
        await this.analyzeLogDirectory(logDir)
      }

      if (this.accounts.size === 0) {
        console.log('❌ 没有找到任何Claude账户的请求记录')
        return []
      }

      // 分析会话窗口
      const results = this.analyzeSessionWindows()

      // 显示结果
      this.displayResults(results)

      // 更新Redis（如果需要）
      if (updateRedis) {
        await this.updateRedisSessionWindows(results, dryRun)
      }

      return results
    } catch (error) {
      console.error('❌ 分析失败:', error)
      throw error
    }
  }
}

// 命令行参数解析
function parseArgs() {
  const args = process.argv.slice(2)
  const options = {
    logDir: './logs',
    singleFile: null,
    updateRedis: false,
    dryRun: true
  }

  for (const arg of args) {
    if (arg.startsWith('--log-dir=')) {
      options.logDir = arg.split('=')[1]
    } else if (arg.startsWith('--file=')) {
      options.singleFile = arg.split('=')[1]
    } else if (arg === '--update-redis') {
      options.updateRedis = true
    } else if (arg === '--no-dry-run') {
      options.dryRun = false
    } else if (arg === '--help' || arg === '-h') {
      showHelp()
      process.exit(0)
    }
  }

  return options
}

// 显示帮助信息
function showHelp() {
  console.log(`
Claude账户会话窗口日志分析工具

从日志文件中分析Claude账户的请求时间，计算会话窗口，并可选择性地更新Redis数据。

用法:
  node scripts/analyze-log-sessions.js [选项]

选项:
  --log-dir=PATH       日志文件目录 (默认: ./logs)
  --file=PATH          分析单个日志文件
  --update-redis       更新Redis中的会话窗口数据
  --no-dry-run         实际执行Redis更新（默认为模拟模式）
  --help, -h           显示此帮助信息

示例:
  # 分析默认日志目录
  node scripts/analyze-log-sessions.js

  # 分析指定目录的日志
  node scripts/analyze-log-sessions.js --log-dir=/path/to/logs

  # 分析单个日志文件
  node scripts/analyze-log-sessions.js --file=/path/to/logfile.log

  # 模拟更新Redis数据（不实际更新）
  node scripts/analyze-log-sessions.js --file=/path/to/logfile.log --update-redis

  # 实际更新Redis数据
  node scripts/analyze-log-sessions.js --file=/path/to/logfile.log --update-redis --no-dry-run

会话窗口规则:
  - Claude官方规定每5小时为一个会话窗口
  - 窗口按整点对齐（如 05:00-10:00, 10:00-15:00）
  - 只有当前时间在窗口内的才被认为是活跃窗口
  - 工具会自动识别并恢复活跃的会话窗口
`)
}

// 主函数
async function main() {
  try {
    const options = parseArgs()

    const analyzer = new LogSessionAnalyzer()
    await analyzer.analyze(options)

    console.log('🎉 分析完成')
  } catch (error) {
    console.error('💥 程序执行失败:', error)
    process.exit(1)
  }
}

// 如果直接运行此脚本
if (require.main === module) {
  main()
}

module.exports = LogSessionAnalyzer
