/**
 * Utility CLI Commands - TypeScript Implementation
 * 
 * Database maintenance, statistics, and benchmarking
 */

import chalk from 'chalk'
import ora from 'ora'
import Table from 'cli-table3'
import { Brainy } from '../../brainy.js'
import { NounType } from '../../types/graphTypes.js'

interface UtilityOptions {
  verbose?: boolean
  json?: boolean
  pretty?: boolean
}

interface StatsOptions extends UtilityOptions {
  byService?: boolean
  detailed?: boolean
}

interface CleanOptions extends UtilityOptions {
  removeOrphans?: boolean
  rebuildIndex?: boolean
}

interface BenchmarkOptions extends UtilityOptions {
  operations?: string
  iterations?: string
}

let brainyInstance: Brainy | null = null

const getBrainy = (): Brainy => {
  if (!brainyInstance) {
    brainyInstance = new Brainy()
  }
  return brainyInstance
}

const formatBytes = (bytes: number): string => {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

const formatOutput = (data: any, options: UtilityOptions): void => {
  if (options.json) {
    console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data))
  }
}

export const utilityCommands = {
  /**
   * Show database statistics
   */
  async stats(options: StatsOptions) {
    const spinner = ora('Gathering statistics...').start()

    try {
      const brain = getBrainy()
      const nounCount = await brain.getNounCount()
      const verbCount = await brain.getVerbCount()
      const memUsage = process.memoryUsage()

      spinner.succeed('Statistics gathered')

      const stats = {
        nounCount,
        verbCount,
        totalItems: nounCount + verbCount
      }

      if (options.json) {
        formatOutput(stats, options)
        return
      }

      console.log(chalk.cyan('\n📊 Database Statistics\n'))

      // Core stats table
      const coreTable = new Table({
        head: [chalk.cyan('Metric'), chalk.cyan('Value')],
        style: { head: [], border: [] }
      })

      coreTable.push(
        ['Total Items', chalk.green(stats.totalItems)],
        ['Nouns', chalk.green(stats.nounCount)],
        ['Verbs (Relationships)', chalk.green(stats.verbCount)]
      )

      console.log(coreTable.toString())

      // Memory usage
      console.log(chalk.cyan('\n🧠 Memory Usage\n'))

      const memTable = new Table({
        head: [chalk.cyan('Type'), chalk.cyan('Size')],
        style: { head: [], border: [] }
      })

      memTable.push(
        ['Heap Used', formatBytes(memUsage.heapUsed)],
        ['Heap Total', formatBytes(memUsage.heapTotal)],
        ['RSS', formatBytes(memUsage.rss)],
        ['External', formatBytes(memUsage.external)]
      )

      console.log(memTable.toString())
      
    } catch (error: any) {
      spinner.fail('Failed to gather statistics')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * Clean and optimize database
   */
  async clean(options: CleanOptions) {
    const spinner = ora('Cleaning database...').start()

    try {
      const brain = getBrainy()

      // For now, only support full clear
      // removeOrphans and rebuildIndex would require new Brainy APIs
      if (options.removeOrphans || options.rebuildIndex) {
        spinner.warn('Advanced cleanup options not yet implemented')
        console.log(chalk.yellow('\n⚠️  Advanced cleanup features coming in v3.21.0:'))
        console.log(chalk.dim('  • --remove-orphans: Remove disconnected items'))
        console.log(chalk.dim('  • --rebuild-index: Rebuild vector index'))
        console.log(chalk.dim('\nUse "brainy clean" without options to clear the database'))
        return
      }

      // Show warning before clearing
      console.log(chalk.yellow('\n⚠️  WARNING: This will permanently delete ALL data!'))
      const dataApi = await brain.data()

      // Clear all data
      spinner.text = 'Clearing all data...'
      await dataApi.clear({ entities: true, relations: true })

      spinner.succeed('Database cleared')

      if (!options.json) {
        console.log(chalk.green('\n✓ Database cleared successfully'))
        console.log(chalk.dim('  All nouns, verbs, and metadata have been removed'))
      } else {
        formatOutput({ cleared: true, success: true }, options)
      }
    } catch (error: any) {
      spinner.fail('Cleanup failed')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * Run performance benchmarks
   */
  async benchmark(options: BenchmarkOptions) {
    const operations = options.operations || 'all'
    const iterations = parseInt(options.iterations || '100')
    
    console.log(chalk.cyan(`\n🚀 Running Benchmarks (${iterations} iterations)\n`))
    
    const results: any = {
      operations: {},
      summary: {}
    }
    
    try {
      const brain = getBrainy()
      
      // Benchmark different operations
      const benchmarks = [
        { name: 'add', enabled: operations === 'all' || operations.includes('add') },
        { name: 'search', enabled: operations === 'all' || operations.includes('search') },
        { name: 'similarity', enabled: operations === 'all' || operations.includes('similarity') },
        { name: 'cluster', enabled: operations === 'all' || operations.includes('cluster') }
      ]
      
      for (const bench of benchmarks) {
        if (!bench.enabled) continue
        
        const spinner = ora(`Benchmarking ${bench.name}...`).start()
        const times: number[] = []
        
        for (let i = 0; i < iterations; i++) {
          const start = Date.now()
          
          switch (bench.name) {
            case 'add':
              await brain.add({ data: `Test item ${i}`, type: NounType.Thing, metadata: { benchmark: true } })
              break
            case 'search':
              await brain.find({ query: 'test', limit: 10 })
              break
            case 'similarity':
              const neural = brain.neural()
              await neural.similar('test1', 'test2')
              break
            case 'cluster':
              const neuralApi = brain.neural()
              await neuralApi.clusters()
              break
          }
          
          times.push(Date.now() - start)
        }
        
        // Calculate statistics
        const avg = times.reduce((a, b) => a + b, 0) / times.length
        const min = Math.min(...times)
        const max = Math.max(...times)
        const median = times.sort((a, b) => a - b)[Math.floor(times.length / 2)]
        
        results.operations[bench.name] = {
          avg: avg.toFixed(2),
          min,
          max,
          median,
          ops: (1000 / avg).toFixed(2)
        }
        
        spinner.succeed(`${bench.name}: ${avg.toFixed(2)}ms avg (${(1000 / avg).toFixed(2)} ops/sec)`)
      }
      
      // Calculate summary
      const totalOps: number = (Object.values(results.operations) as any[]).reduce((sum: number, op: any) =>
        sum + parseFloat(op.ops), 0)

      results.summary = {
        totalOperations: Object.keys(results.operations).length,
        averageOpsPerSec: totalOps > 0 ? (totalOps / Object.keys(results.operations).length).toFixed(2) : '0'
      }
      
      if (!options.json) {
        // Display results table
        console.log(chalk.cyan('\n📊 Benchmark Results\n'))
        
        const table = new Table({
          head: [
            chalk.cyan('Operation'),
            chalk.cyan('Avg (ms)'),
            chalk.cyan('Min (ms)'),
            chalk.cyan('Max (ms)'),
            chalk.cyan('Median (ms)'),
            chalk.cyan('Ops/sec')
          ],
          style: { head: [], border: [] }
        })
        
        Object.entries(results.operations).forEach(([op, stats]: [string, any]) => {
          table.push([
            op,
            stats.avg,
            stats.min,
            stats.max,
            stats.median,
            chalk.green(stats.ops)
          ])
        })
        
        console.log(table.toString())
        
        console.log(chalk.cyan('\n📈 Summary'))
        console.log(`  Operations tested: ${results.summary.totalOperations}`)
        console.log(`  Average throughput: ${chalk.green(results.summary.averageOpsPerSec)} ops/sec`)
      } else {
        formatOutput(results, options)
      }
      
    } catch (error: any) {
      console.error(chalk.red('Benchmark failed:'), error.message)
      process.exit(1)
    }
  }
}