/**
 * Threads CLI command — Thread lifecycle management
 *
 * Usage:
 *   morph-spec threads list [--feature=<f>] [--status=<s>]
 *   morph-spec threads status <id>
 *   morph-spec threads kill <id>
 *   morph-spec threads analytics [--feature=<f>]
 *   morph-spec threads wait-all <feature>
 *   morph-spec threads wait-any <feature>
 */

import chalk from 'chalk';
import {
  listThreads, getThread, killThread, getThreadAnalytics,
  THREAD_STATUS, THREAD_TYPES
} from '../../lib/threads/thread-manager.js';
import { generateAsciiChart, generateTimelineChart } from '../../lib/analytics/analytics-engine.js';

const STATUS_COLORS = {
  [THREAD_STATUS.PENDING]: chalk.yellow,
  [THREAD_STATUS.RUNNING]: chalk.cyan,
  [THREAD_STATUS.COMPLETED]: chalk.green,
  [THREAD_STATUS.FAILED]: chalk.red,
  [THREAD_STATUS.KILLED]: chalk.gray
};

const TYPE_ICONS = {
  [THREAD_TYPES.BASE]: '◆',
  [THREAD_TYPES.PARALLEL]: '⫸',
  [THREAD_TYPES.FUSION]: '⊕',
  [THREAD_TYPES.LONG_RUNNING]: '∞',
  [THREAD_TYPES.ZERO_TOUCH]: '⚡'
};

function formatDuration(seconds) {
  if (!seconds) return '—';
  if (seconds < 60) return `${seconds}s`;
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
  return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
}

/**
 * List threads
 */
export async function threadsListCommand(options) {
  try {
    const threads = listThreads({
      feature: options.feature,
      status: options.status,
      type: options.type
    });

    if (threads.length === 0) {
      console.log(chalk.gray('No threads found.'));
      return;
    }

    console.log(chalk.cyan(`\n  Threads (${threads.length})\n`));
    console.log(
      '  ' + 'ID'.padEnd(10) + ' ' +
      'Type'.padEnd(14) + ' ' +
      'Status'.padEnd(12) + ' ' +
      'Feature'.padEnd(20) + ' ' +
      'Agent'.padEnd(20) + ' ' +
      'Duration'
    );
    console.log('  ' + '─'.repeat(90));

    for (const t of threads) {
      const icon = TYPE_ICONS[t.type] || '◆';
      const colorFn = STATUS_COLORS[t.status] || chalk.white;
      const shortId = t.id.substring(0, 8);

      console.log(
        '  ' +
        chalk.gray(shortId) + ' ' +
        `${icon} ${t.type}`.padEnd(14) + ' ' +
        colorFn(t.status.padEnd(12)) + ' ' +
        (t.feature || '—').padEnd(20) + ' ' +
        (t.agent || '—').padEnd(20) + ' ' +
        formatDuration(t.duration)
      );
    }
    console.log('');
  } catch (err) {
    console.error(chalk.red(`Error: ${err.message}`));
    process.exit(1);
  }
}

/**
 * Show thread status detail
 */
export async function threadsStatusCommand(id) {
  try {
    const thread = getThread(id);
    if (!thread) {
      console.error(chalk.red(`Thread not found: ${id}`));
      process.exit(1);
    }

    const colorFn = STATUS_COLORS[thread.status] || chalk.white;
    const icon = TYPE_ICONS[thread.type] || '◆';

    console.log(chalk.cyan('\n  Thread Detail\n'));
    console.log(`  ID:       ${chalk.gray(thread.id)}`);
    console.log(`  Type:     ${icon} ${thread.type}`);
    console.log(`  Status:   ${colorFn(thread.status)}`);
    console.log(`  Feature:  ${thread.feature || '—'}`);
    console.log(`  Agent:    ${thread.agent || '—'}`);
    console.log(`  Mission:  ${thread.mission || '—'}`);
    console.log(`  Created:  ${thread.createdAt}`);
    console.log(`  Started:  ${thread.startedAt || '—'}`);
    console.log(`  Ended:    ${thread.completedAt || '—'}`);
    console.log(`  Duration: ${formatDuration(thread.duration)}`);

    if (thread.metrics) {
      console.log('\n  Metrics:');
      console.log(`    Tool calls:     ${thread.metrics.toolCalls || 0}`);
      console.log(`    Tokens used:    ${(thread.metrics.tokensUsed || 0).toLocaleString()}`);
      console.log(`    Checkpoints OK: ${thread.metrics.checkpointsPassed || 0}`);
      console.log(`    Checkpoints KO: ${thread.metrics.checkpointsFailed || 0}`);
    }

    if (thread.events?.length > 0) {
      console.log(`\n  Recent events (${thread.events.length}):`);
      thread.events.slice(-5).forEach(e => {
        console.log(`    ${chalk.gray(e.timestamp)} ${e.type}`);
      });
    }

    if (thread.failureReason) {
      console.log(`\n  ${chalk.red('Failure:')} ${thread.failureReason}`);
    }
    console.log('');
  } catch (err) {
    console.error(chalk.red(`Error: ${err.message}`));
    process.exit(1);
  }
}

/**
 * Kill a thread
 */
export async function threadsKillCommand(id) {
  try {
    const thread = killThread(id);
    console.log(chalk.yellow(`✓ Thread ${id.substring(0, 8)} killed.`));
    console.log(`  Status: ${thread.status} | Duration: ${formatDuration(thread.duration)}`);
  } catch (err) {
    console.error(chalk.red(`Error: ${err.message}`));
    process.exit(1);
  }
}

/**
 * Show threads analytics dashboard
 */
export async function threadsAnalyticsCommand(options) {
  try {
    const feature = options.feature;
    if (!feature) {
      console.error(chalk.red('--feature is required for analytics'));
      process.exit(1);
    }

    const analytics = getThreadAnalytics(feature);

    console.log(chalk.cyan(`\n  Thread Analytics — ${feature}\n`));
    console.log(`  Total threads:   ${analytics.total}`);
    console.log(`  By status:`);
    for (const [status, count] of Object.entries(analytics.byStatus)) {
      const colorFn = STATUS_COLORS[status] || chalk.white;
      console.log(`    ${colorFn(status.padEnd(12))} ${count}`);
    }

    console.log('\n  By type:');
    for (const [type, count] of Object.entries(analytics.byType)) {
      const icon = TYPE_ICONS[type] || '◆';
      console.log(`    ${icon} ${type.padEnd(14)} ${count}`);
    }

    console.log(`\n  Avg duration:    ${formatDuration(analytics.avgDuration)}`);
    console.log(`  Total tool calls: ${analytics.totalToolCalls}`);
    console.log(`  Checkpoint pass rate: ${Math.round(analytics.checkpointPassRate * 100)}%`);

    if (analytics.total > 0) {
      const statusChart = generateAsciiChart(analytics.byStatus, { title: 'Threads by Status' });
      console.log('\n' + statusChart);
    }
    console.log('');
  } catch (err) {
    console.error(chalk.red(`Error: ${err.message}`));
    process.exit(1);
  }
}

/**
 * Wait for all threads in a feature to complete
 */
export async function threadsWaitAllCommand(feature, options) {
  const timeoutMs = (options.timeout || 60) * 60 * 1000;
  const pollInterval = 5000;
  const start = Date.now();

  console.log(chalk.cyan(`\n  Waiting for all threads in: ${feature}\n`));

  while (Date.now() - start < timeoutMs) {
    const threads = listThreads({ feature });
    const active = threads.filter(t =>
      t.status === THREAD_STATUS.PENDING || t.status === THREAD_STATUS.RUNNING
    );

    if (active.length === 0) {
      const failed = threads.filter(t => t.status === THREAD_STATUS.FAILED);
      if (failed.length > 0) {
        console.log(chalk.yellow(`  ⚠ ${failed.length} thread(s) failed`));
        process.exit(1);
      }
      console.log(chalk.green(`  ✓ All ${threads.length} threads completed`));
      return;
    }

    process.stdout.write(`\r  Still running: ${active.length} thread(s)...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }

  console.error(chalk.red('\n  Timeout waiting for threads'));
  process.exit(1);
}

/**
 * Wait for any thread to complete
 */
export async function threadsWaitAnyCommand(feature, options) {
  const timeoutMs = (options.timeout || 60) * 60 * 1000;
  const pollInterval = 3000;
  const start = Date.now();

  console.log(chalk.cyan(`\n  Waiting for first thread to complete in: ${feature}\n`));

  while (Date.now() - start < timeoutMs) {
    const threads = listThreads({ feature });
    const done = threads.find(t =>
      t.status === THREAD_STATUS.COMPLETED || t.status === THREAD_STATUS.FAILED
    );

    if (done) {
      const colorFn = STATUS_COLORS[done.status] || chalk.white;
      console.log(`  ${colorFn('✓')} Thread ${done.id.substring(0, 8)} ${done.status}`);
      if (done.agent) console.log(`    Agent: ${done.agent}`);
      return;
    }

    await new Promise(r => setTimeout(r, pollInterval));
  }

  console.error(chalk.red('\n  Timeout waiting for thread'));
  process.exit(1);
}
