/**
 * CLI utility functions for the Ion workflow engine.
 *
 * Provides formatting, table rendering, and JSON output helpers
 * used across all CLI commands.
 */

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface CliOptions {
  /** Working directory override. */
  cwd?: string;
  /** Output as JSON (suppresses all other output). */
  json?: boolean;
  /** Path to the workflow store database. */
  store?: string;
  /** Path to the database file. */
  dbPath?: string;
  /** Output file path (for convert command). */
  output?: string;
}

// ---------------------------------------------------------------------------
// Duration formatting
// ---------------------------------------------------------------------------

/**
 * Format a duration in milliseconds into a human-readable string.
 *
 * @example
 *   formatDuration(90500) // "1m 30s"
 *   formatDuration(3661000) // "1h 1m"
 *   formatDuration(500) // "0s"
 */
export function formatDuration(ms: number): string {
  if (ms < 0) ms = 0;

  const seconds = Math.floor(ms / 1000) % 60;
  const minutes = Math.floor(ms / 60000) % 60;
  const hours = Math.floor(ms / 3600000);

  if (hours > 0) {
    return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
  }
  if (minutes > 0) {
    return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
  }
  return `${seconds}s`;
}

// ---------------------------------------------------------------------------
// Timestamp formatting
// ---------------------------------------------------------------------------

/**
 * Format a Date into an ISO-like timestamp suitable for CLI display.
 *
 * @example
 *   formatTimestamp(new Date('2025-06-07T14:30:00Z'))
 *   // "2025-06-07 14:30:00"
 */
export function formatTimestamp(date: Date): string {
  const y = date.getFullYear();
  const mo = String(date.getMonth() + 1).padStart(2, '0');
  const d = String(date.getDate()).padStart(2, '0');
  const h = String(date.getHours()).padStart(2, '0');
  const mi = String(date.getMinutes()).padStart(2, '0');
  const s = String(date.getSeconds()).padStart(2, '0');
  return `${y}-${mo}-${d} ${h}:${mi}:${s}`;
}

// ---------------------------------------------------------------------------
// String truncation
// ---------------------------------------------------------------------------

/**
 * Truncate a string to `max` characters, appending an ellipsis if truncated.
 *
 * @example
 *   truncate('hello world', 8) // "hello..."
 *   truncate('hi', 8) // "hi"
 */
export function truncate(str: string, max: number): string {
  if (str.length <= max) return str;
  if (max <= 3) return str.slice(0, max);
  return str.slice(0, max - 3) + '...';
}

// ---------------------------------------------------------------------------
// Table rendering
// ---------------------------------------------------------------------------

export interface TableColumn {
  /** Column header label. */
  header: string;
  /** Minimum column width. */
  minWidth?: number;
  /** Field name to extract from each row object. */
  field: string;
}

/**
 * Print a formatted table to stdout.
 *
 * @param rows   - Array of row objects.
 * @param columns - Column definitions with header labels and field names.
 *
 * @example
 *   printTable(
 *     [{ name: 'deploy', desc: 'Deploy app' }],
 *     [{ header: 'Name', field: 'name' }, { header: 'Description', field: 'desc' }],
 *   )
 */
export function printTable(
  rows: Record<string, unknown>[],
  columns: TableColumn[],
): void {
  if (rows.length === 0) {
    console.log('(no results)');
    return;
  }

  // Compute column widths.
  const widths: number[] = columns.map((col) => {
    const headerLen = col.header.length;
    const dataLen = Math.max(
      ...rows.map((row) => {
        const val = row[col.field];
        const str = val === undefined || val === null ? '' : String(val);
        return str.length;
      }),
      0,
    );
    const min = col.minWidth ?? 0;
    return Math.max(headerLen, dataLen, min);
  });

  // Header row.
  const headerLine = columns
    .map((col, i) => col.header.padEnd(widths[i]!))
    .join('  ');
  console.log(headerLine);

  // Separator.
  const sepLine = widths.map((w) => '-'.repeat(w)).join('  ');
  console.log(sepLine);

  // Data rows.
  for (const row of rows) {
    const line = columns
      .map((col, i) => {
        const val = row[col.field];
        const str = val === undefined || val === null ? '' : String(val);
        return str.padEnd(widths[i]!);
      })
      .join('  ');
    console.log(line);
  }
}

// ---------------------------------------------------------------------------
// JSON output
// ---------------------------------------------------------------------------

/**
 * Print a data structure as formatted JSON to stdout.
 * Uses 2-space indentation.
 */
export function printJson(data: unknown): void {
  console.log(JSON.stringify(data, null, 2));
}

// ---------------------------------------------------------------------------
// Argument parsing
// ---------------------------------------------------------------------------

/**
 * Parse CLI arguments into positional args and named options.
 *
 * Supports `--flag` (boolean) and `--key value` (string) formats.
 * Everything after `--` is treated as positional.
 *
 * @example
 *   parseArgs(['run', 'deploy', '--json', '--cwd', '/tmp'])
 *   // { args: ['run', 'deploy'], options: { json: true, cwd: '/tmp' } }
 */
export function parseArgs(argv: string[]): {
  args: string[];
  options: Record<string, string | boolean>;
} {
  const args: string[] = [];
  const options: Record<string, string | boolean> = {};
  let i = 0;

  while (i < argv.length) {
    const token = argv[i]!;

    if (token === '--') {
      args.push(...argv.slice(i + 1));
      break;
    }

    if (token.startsWith('--')) {
      const key = token.slice(2);
      const next = argv[i + 1];

      if (next && !next.startsWith('--')) {
        options[key] = next;
        i += 2;
      } else {
        options[key] = true;
        i += 1;
      }
    } else {
      args.push(token);
      i += 1;
    }
  }

  return { args, options };
}

/**
 * Build a CliOptions object from parsed options.
 * Extracts known CLI flags into their typed fields.
 */
export function buildCliOptions(
  options: Record<string, string | boolean>,
): CliOptions {
  return {
    cwd: typeof options.cwd === 'string' ? options.cwd : undefined,
    json: options.json === true,
    store: typeof options.store === 'string' ? options.store : undefined,
    dbPath: typeof options['db-path'] === 'string' ? options['db-path'] : undefined,
    output: typeof options.output === 'string' ? options.output : undefined,
  };
}