/**
 * @module cli/commands/data
 * @description Detailed database statistics for the `data-stats` CLI command.
 *
 * Renders `brain.stats()` (the operator-facing `BrainyStats` summary) as a
 * human-readable report: counts with per-type breakdowns, indexed fields,
 * per-index health flags, storage backend, writer-lock holder, and library
 * version. Pass `--json` for machine-readable output.
 *
 * The legacy backup/import/export facade that used to live behind this
 * command was superseded in 8.0: snapshots are `brainy snapshot` / `brainy
 * restore` (the Db API's `persist()`/`restore()`), and data ingestion is
 * `brainy import` (`brain.import()`).
 */

import chalk from 'chalk'
import ora from 'ora'
import { Brainy } from '../../brainy.js'

/**
 * @description Shared CLI flags the data commands accept (`--verbose`,
 * `--json`, `--pretty`), mirroring the other command modules.
 */
interface DataOptions {
  verbose?: boolean
  json?: boolean
  pretty?: boolean
}

let brainyInstance: Brainy | null = null

/**
 * @description Lazily construct the module's shared `Brainy` instance so
 * repeated handler invocations (and tests) reuse one store handle.
 * @returns The shared `Brainy` instance.
 */
const getBrainy = (): Brainy => {
  if (!brainyInstance) {
    brainyInstance = new Brainy()
  }
  return brainyInstance
}

/**
 * @description Emit machine-readable output when `--json` is set; honors
 * `--pretty` for indented JSON.
 * @param data - The value to serialize.
 * @param options - Shared CLI flags.
 */
const formatOutput = (data: unknown, options: DataOptions): void => {
  if (options.json) {
    console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data))
  }
}

/**
 * @description Render a `Record<type, count>` breakdown as indented,
 * count-sorted lines (largest first).
 * @param byType - Per-type counts, e.g. `BrainyStats.entitiesByType`.
 * @returns Formatted lines ready for `console.log`, one per type.
 */
const formatTypeBreakdown = (byType: Record<string, number>): string[] =>
  Object.entries(byType)
    .sort(([, a], [, b]) => b - a)
    .map(([type, count]) => `  ${type}: ${chalk.green(count)}`)

/**
 * @description The `data-stats` command handler registered in
 * `src/cli/index.ts`.
 */
export const dataCommands = {
  /**
   * @description Show detailed database statistics via `brain.stats()`:
   * entity/relationship totals with per-type breakdowns, indexed metadata
   * fields, index health, storage backend, writer lock, and version.
   * @param options - Shared CLI flags.
   * @example
   * ```bash
   * brainy data-stats            # human-readable report
   * brainy data-stats --json     # raw BrainyStats JSON
   * ```
   */
  async stats(options: DataOptions) {
    const spinner = ora('Gathering statistics...').start()

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

      spinner.succeed('Statistics gathered')

      if (options.json) {
        formatOutput(stats, options)
        // close() releases the writer lock and indexes, but global timers
        // (UnifiedCache bookkeeping, PathResolver stats) keep the event loop
        // alive. CLI commands are one-shot — exit explicitly.
        await brain.close()
        process.exit(0)
      }

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

      console.log(chalk.bold('Entities:'))
      console.log(`  Total: ${chalk.green(stats.entityCount)}`)
      for (const line of formatTypeBreakdown(stats.entitiesByType)) {
        console.log(`  ${line}`)
      }

      console.log(chalk.bold('\nRelationships:'))
      console.log(`  Total: ${chalk.green(stats.relationCount)}`)
      for (const line of formatTypeBreakdown(stats.relationsByType)) {
        console.log(`  ${line}`)
      }

      console.log(chalk.bold('\nIndexes:'))
      console.log(`  Indexed fields: ${chalk.green(stats.fieldRegistry.length)}`)
      if (options.verbose && stats.fieldRegistry.length > 0) {
        console.log(chalk.dim(`    ${stats.fieldRegistry.join(', ')}`))
      }
      const health = (ok: boolean) => (ok ? chalk.green('ok') : chalk.red('empty'))
      console.log(`  Vector: ${health(stats.indexHealth.vector)}`)
      console.log(`  Metadata: ${health(stats.indexHealth.metadata)}`)
      console.log(`  Graph: ${health(stats.indexHealth.graph)}`)

      console.log(chalk.bold('\nStorage:'))
      console.log(`  Backend: ${stats.storage.backend}`)
      if (stats.storage.rootDir) {
        console.log(`  Root: ${chalk.dim(stats.storage.rootDir)}`)
      }

      if (stats.writerLock) {
        console.log(chalk.bold('\nWriter Lock:'))
        console.log(
          `  PID ${stats.writerLock.pid} on ${stats.writerLock.hostname} ` +
            chalk.dim(`(since ${stats.writerLock.startedAt})`)
        )
      }

      console.log(chalk.dim(`\nMode: ${stats.mode} • Brainy v${stats.version}`))

      // close() releases the writer lock and indexes, but global timers
      // (UnifiedCache bookkeeping, PathResolver stats) keep the event loop
      // alive. CLI commands are one-shot — exit explicitly.
      await brain.close()
      process.exit(0)
    } catch (error: unknown) {
      spinner.fail('Failed to get statistics')
      console.error(chalk.red((error as Error).message))
      process.exit(1)
    }
  }
}
