/**
 * VFS CLI Commands - Virtual File System Operations
 *
 * Complete filesystem-like interface for Brainy's VFS
 */

import chalk from 'chalk'
import ora from 'ora'
import Table from 'cli-table3'
import { readFileSync, writeFileSync } from 'node:fs'
import { Brainy } from '../../brainy.js'

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

let brainyInstance: Brainy | null = null

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

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

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 formatDate = (date: Date): string => {
  return date.toLocaleString()
}

export const vfsCommands = {
  /**
   * Read file from VFS
   */
  async read(path: string, options: VFSOptions & { output?: string; encoding?: string }) {
    const spinner = ora('Reading file...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      const buffer = await vfs.readFile(path, {
        encoding: options.encoding as any
      })

      spinner.succeed('File read successfully')

      if (options.output) {
        // Write to local filesystem
        writeFileSync(options.output, buffer)
        console.log(chalk.green(`✓ Saved to: ${options.output}`))
      } else if (!options.json) {
        // Display content
        console.log('\n' + buffer.toString())
      } else {
        formatOutput({ path, content: buffer.toString(), size: buffer.length }, options)
      }
    } catch (error: any) {
      spinner.fail('Failed to read file')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * Write file to VFS
   */
  async write(path: string, options: VFSOptions & { content?: string; file?: string; encoding?: string }) {
    const spinner = ora('Writing file...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      let data: string
      if (options.file) {
        // Read from local file
        data = readFileSync(options.file, 'utf-8')
      } else if (options.content) {
        data = options.content
      } else {
        spinner.fail('Must provide --content or --file')
        process.exit(1)
      }

      await vfs.writeFile(path, data, {
        encoding: options.encoding as any
      })

      spinner.succeed('File written successfully')

      if (!options.json) {
        console.log(chalk.green(`✓ Written to: ${path}`))
        console.log(chalk.dim(`  Size: ${formatBytes(Buffer.byteLength(data))}`))
      } else {
        formatOutput({ path, size: Buffer.byteLength(data) }, options)
      }
    } catch (error: any) {
      spinner.fail('Failed to write file')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * List directory contents
   */
  async ls(path: string, options: VFSOptions & { long?: boolean; all?: boolean }) {
    const spinner = ora('Listing directory...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      const entries = await vfs.readdir(path, { withFileTypes: true })

      spinner.succeed(`Found ${Array.isArray(entries) ? entries.length : 0} items`)

      if (!options.json) {
        if (!Array.isArray(entries) || entries.length === 0) {
          console.log(chalk.yellow('Directory is empty'))
          return
        }

        if (options.long) {
          // Long format with details
          const table = new Table({
            head: [chalk.cyan('Type'), chalk.cyan('Size'), chalk.cyan('Modified'), chalk.cyan('Name')],
            style: { head: [], border: [] }
          })

          for (const entry of entries as any[]) {
            if (!options.all && entry.name.startsWith('.')) continue

            const stat = await vfs.stat(`${path}/${entry.name}`)
            table.push([
              entry.isDirectory() ? chalk.blue('DIR') : 'FILE',
              entry.isDirectory() ? '-' : formatBytes(stat.size),
              formatDate(stat.mtime),
              entry.name
            ])
          }

          console.log('\n' + table.toString())
        } else {
          // Simple format
          console.log()
          for (const entry of entries as any[]) {
            if (!options.all && entry.name.startsWith('.')) continue

            if (entry.isDirectory()) {
              console.log(chalk.blue(entry.name + '/'))
            } else {
              console.log(entry.name)
            }
          }
        }
      } else {
        formatOutput(entries, options)
      }
    } catch (error: any) {
      spinner.fail('Failed to list directory')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * Get file/directory stats
   */
  async stat(path: string, options: VFSOptions) {
    const spinner = ora('Getting file stats...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      const stats = await vfs.stat(path)

      spinner.succeed('Stats retrieved')

      if (!options.json) {
        console.log(chalk.cyan('\nFile Statistics:'))
        console.log(`  Path: ${path}`)
        console.log(`  Type: ${stats.isDirectory() ? chalk.blue('Directory') : 'File'}`)
        console.log(`  Size: ${formatBytes(stats.size)}`)
        console.log(`  Created: ${formatDate(stats.birthtime)}`)
        console.log(`  Modified: ${formatDate(stats.mtime)}`)
        console.log(`  Accessed: ${formatDate(stats.atime)}`)
      } else {
        formatOutput(stats, options)
      }
    } catch (error: any) {
      spinner.fail('Failed to get stats')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * Create directory
   */
  async mkdir(path: string, options: VFSOptions & { parents?: boolean }) {
    const spinner = ora('Creating directory...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      await vfs.mkdir(path, { recursive: options.parents })

      spinner.succeed('Directory created')

      if (!options.json) {
        console.log(chalk.green(`✓ Created: ${path}`))
      } else {
        formatOutput({ path, created: true }, options)
      }
    } catch (error: any) {
      spinner.fail('Failed to create directory')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * Remove file or directory
   */
  async rm(path: string, options: VFSOptions & { recursive?: boolean; force?: boolean }) {
    const spinner = ora('Removing...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      const stats = await vfs.stat(path)

      if (stats.isDirectory()) {
        await vfs.rmdir(path, { recursive: options.recursive })
      } else {
        await vfs.unlink(path)
      }

      spinner.succeed('Removed successfully')

      if (!options.json) {
        console.log(chalk.green(`✓ Removed: ${path}`))
      } else {
        formatOutput({ path, removed: true }, options)
      }
    } catch (error: any) {
      spinner.fail('Failed to remove')
      console.error(chalk.red(error.message))
      if (!options.force) {
        process.exit(1)
      }
    }
  },

  /**
   * Search files by content
   */
  async search(query: string, options: VFSOptions & { path?: string; limit?: string; type?: string }) {
    const spinner = ora('Searching files...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      const results = await vfs.search(query, {
        path: options.path,
        limit: options.limit ? parseInt(options.limit) : 10
      })

      spinner.succeed(`Found ${results.length} results`)

      if (!options.json) {
        if (results.length === 0) {
          console.log(chalk.yellow('No results found'))
        } else {
          console.log(chalk.cyan('\n📄 Search Results:\n'))

          results.forEach((result, i) => {
            console.log(chalk.bold(`${i + 1}. ${result.path}`))
            if (result.score) {
              console.log(chalk.dim(`   Score: ${(result.score * 100).toFixed(1)}%`))
            }
            if ((result as any).excerpt) {
              console.log(chalk.dim(`   ${(result as any).excerpt}`))
            }
            console.log()
          })
        }
      } else {
        formatOutput(results, options)
      }
    } catch (error: any) {
      spinner.fail('Search failed')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * Find similar files
   */
  async similar(path: string, options: VFSOptions & { limit?: string; threshold?: string }) {
    const spinner = ora('Finding similar files...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      const results = await vfs.findSimilar(path, {
        limit: options.limit ? parseInt(options.limit) : 10,
        threshold: options.threshold ? parseFloat(options.threshold) : 0.7
      })

      spinner.succeed(`Found ${results.length} similar files`)

      if (!options.json) {
        if (results.length === 0) {
          console.log(chalk.yellow('No similar files found'))
        } else {
          console.log(chalk.cyan('\n🔗 Similar Files:\n'))

          results.forEach((result, i) => {
            console.log(chalk.bold(`${i + 1}. ${result.path}`))
            if (result.score) {
              console.log(chalk.green(`   Similarity: ${(result.score * 100).toFixed(1)}%`))
            }
            console.log()
          })
        }
      } else {
        formatOutput(results, options)
      }
    } catch (error: any) {
      spinner.fail('Failed to find similar files')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  },

  /**
   * Get directory tree structure
   */
  async tree(path: string, options: VFSOptions & { depth?: string }) {
    const spinner = ora('Building tree...').start()

    try {
      const brain = getBrainy()
      const vfs = brain.vfs()
      await vfs.init()

      const tree = await vfs.getTreeStructure(path, {
        maxDepth: options.depth ? parseInt(options.depth) : 3
      })

      spinner.succeed('Tree built')

      if (!options.json) {
        console.log(chalk.cyan(`\n📁 ${path}\n`))
        displayTree(tree, '', true)
      } else {
        formatOutput(tree, options)
      }
    } catch (error: any) {
      spinner.fail('Failed to build tree')
      console.error(chalk.red(error.message))
      process.exit(1)
    }
  }
}

function displayTree(node: any, prefix: string, isLast: boolean) {
  const connector = isLast ? '└── ' : '├── '
  const name = node.isDirectory ? chalk.blue(node.name + '/') : node.name

  console.log(prefix + connector + name)

  if (node.children && node.children.length > 0) {
    const childPrefix = prefix + (isLast ? '    ' : '│   ')
    node.children.forEach((child: any, i: number) => {
      displayTree(child, childPrefix, i === node.children.length - 1)
    })
  }
}