/**
 * Agent Spawning Core Test Suite
 *
 * Comprehensive test coverage for agent-spawn.ts covering:
 * - Argument parsing and validation
 * - Agent type resolution
 * - Task ID propagation
 * - Context injection (broadcast messages)
 * - Provider parameter handling
 * - Error handling for invalid agent types
 * - Process spawning and monitoring
 * - Exit code handling
 *
 * Target Coverage: ≥80%
 *
 * @version 1.0.0
 * @description Integration tests for critical agent spawning logic (10% → 80%+ coverage)
 * @note Tests CLI via tsx execution (works around ES module import issues in Jest)
 */

import { describe, test, expect } from '@jest/globals';
import { spawn } from 'child_process';
import * as path from 'path';

// Type definitions for better test clarity
interface SpawnResult {
  exitCode: number | null;
  signal: string | null;
  stdout: string;
  stderr: string;
}

// Helper to spawn the CLI and collect output
function spawnCLI(args: string[], env: Record<string, string> = {}): Promise<SpawnResult> {
  return new Promise((resolve) => {
    const cliPath = path.resolve(__dirname, '../../src/cli/agent-spawn.ts');

    const proc = spawn('tsx', [cliPath, ...args], {
      env: { ...process.env, ...env },
      cwd: path.resolve(__dirname, '../..'),
    });

    let stdout = '';
    let stderr = '';

    if (proc.stdout) {
      proc.stdout.on('data', (data) => {
        stdout += data.toString();
      });
    }

    if (proc.stderr) {
      proc.stderr.on('data', (data) => {
        stderr += data.toString();
      });
    }

    proc.on('exit', (code, signal) => {
      resolve({
        exitCode: code,
        signal,
        stdout,
        stderr,
      });
    });

    proc.on('error', (err) => {
      stderr += err.message;
      resolve({
        exitCode: 1,
        signal: null,
        stdout,
        stderr,
      });
    });

    // Kill spawned child processes after short delay
    setTimeout(() => {
      if (!proc.killed) {
        proc.kill('SIGTERM');
      }
    }, 3000);
  });
}

describe('Agent Spawning Core - agent-spawn.ts', () => {
  const testTimeout = 10000; // 10 second timeout for integration tests

  // ============================================================================
  // Argument Parsing Tests
  // ============================================================================

  describe('Argument Parsing', () => {
    test('parses agent type from "agent <type>" pattern', async () => {
      const result = await spawnCLI(['agent', 'researcher', '--task-id', 'task-test']);

      expect(result.stdout).toContain('Spawning agent: researcher');
      expect(result.stdout).toContain('Task ID: task-test');
    }, testTimeout);

    test('parses agent type from "<type>" pattern (implied agent)', async () => {
      const result = await spawnCLI(['researcher', '--task-id', 'task-123']);

      expect(result.stdout).toContain('Spawning agent: researcher');
      expect(result.stdout).toContain('Task ID: task-123');
    }, testTimeout);

    test('parses all optional parameters correctly', async () => {
      const result = await spawnCLI([
        'backend-developer',
        '--agent-id', 'agent-001',
        '--task-id', 'task-123',
        '--iteration', '5',
        '--context', 'Implement JWT auth',
        '--mode', 'cli',
        '--priority', '8',
        '--parent-task-id', 'parent-456',
      ]);

      expect(result.stdout).toContain('Spawning agent: backend-developer');
      expect(result.stdout).toContain('Agent ID: agent-001');
      expect(result.stdout).toContain('Task ID: task-123');
      expect(result.stdout).toContain('Iteration: 5');
      expect(result.stdout).toContain('Context: Implement JWT auth');
      expect(result.stdout).toContain('Mode: cli');
    }, testTimeout);

    test('handles --parent-task alias for --parent-task-id', async () => {
      const result = await spawnCLI(['tester', '--parent-task', 'parent-789']);

      expect(result.stdout).toContain('--parent-task-id parent-789');
    }, testTimeout);

    test('parses integer values correctly', async () => {
      const result = await spawnCLI(['coder', '--iteration', '42', '--priority', '3']);

      expect(result.stdout).toContain('Iteration: 42');
    }, testTimeout);

    test('warns on unknown options', async () => {
      const result = await spawnCLI(['researcher', '--unknown-flag', 'value']);

      expect(result.stdout).toContain('Unknown option: --unknown-flag');
    }, testTimeout);

    test('exits with error when agent type is missing', async () => {
      const result = await spawnCLI(['--task-id', 'task-123']);

      expect(result.exitCode).toBe(1);
      expect(result.stderr).toContain('Agent type is required');
    }, testTimeout);

    test('handles empty arguments array', async () => {
      const result = await spawnCLI([]);

      expect(result.exitCode).toBe(1);
      expect(result.stderr).toContain('Agent type is required');
    }, testTimeout);

    test('handles multiple parameters in sequence', async () => {
      const result = await spawnCLI([
        'frontend-designer',
        '--task-id', 'ui-task',
        '--iteration', '2',
        '--context', 'Design login page',
      ]);

      expect(result.stdout).toContain('Spawning agent: frontend-designer');
      expect(result.stdout).toContain('Task ID: ui-task');
      expect(result.stdout).toContain('Iteration: 2');
      expect(result.stdout).toContain('Context: Design login page');
    }, testTimeout);

    test('handles special characters in agent type', async () => {
      const result = await spawnCLI(['rust-developer', '--task-id', 'special-123']);

      expect(result.stdout).toContain('Spawning agent: rust-developer');
    }, testTimeout);
  });

  // ============================================================================
  // Help Display Tests
  // ============================================================================

  describe('Help Display', () => {
    test('shows help with --help flag', async () => {
      const result = await spawnCLI(['--help']);

      expect(result.stdout).toContain('cfn-spawn - Claude Flow Novice Agent Spawner');
      expect(result.stdout).toContain('Usage:');
      expect(result.stdout).toContain('Options:');
      expect(result.stdout).toContain('Examples:');
      expect(result.exitCode).toBe(0);
    }, testTimeout);

    test('shows help with -h flag', async () => {
      const result = await spawnCLI(['-h']);

      expect(result.stdout).toContain('cfn-spawn');
      expect(result.stdout).toContain('Usage:');
      expect(result.exitCode).toBe(0);
    }, testTimeout);

    test('help includes all option descriptions', async () => {
      const result = await spawnCLI(['--help']);

      expect(result.stdout).toContain('--agent-id');
      expect(result.stdout).toContain('--task-id');
      expect(result.stdout).toContain('--iteration');
      expect(result.stdout).toContain('--context');
      expect(result.stdout).toContain('--mode');
      expect(result.stdout).toContain('--priority');
      expect(result.stdout).toContain('--parent-task-id');
    }, testTimeout);
  });

  // ============================================================================
  // Process Spawning Tests
  // ============================================================================

  describe('Process Spawning', () => {
    test('logs execution command', async () => {
      const result = await spawnCLI(['researcher', '--task-id', 'task-456']);

      expect(result.stdout).toContain('[cfn-spawn] Executing: npx');
      expect(result.stdout).toContain('claude-flow-novice agent researcher');
    }, testTimeout);

    test('spawns with all parameters in command', async () => {
      const result = await spawnCLI([
        'backend-developer',
        '--agent-id', 'dev-123',
        '--task-id', 'task-789',
        '--iteration', '3',
      ]);

      expect(result.stdout).toContain('--agent-id dev-123');
      expect(result.stdout).toContain('--task-id task-789');
      expect(result.stdout).toContain('--iteration 3');
    }, testTimeout);

    test('handles minimum required parameters', async () => {
      const result = await spawnCLI(['reviewer']);

      expect(result.stdout).toContain('Spawning agent: reviewer');
      expect(result.stdout).toContain('Executing: npx');
    }, testTimeout);
  });

  // ============================================================================
  // Environment Variable Tests
  // ============================================================================

  describe('Environment Variables', () => {
    test('accepts CFN environment variables', async () => {
      const result = await spawnCLI(
        ['tester', '--task-id', 'env-test'],
        {
          CFN_REDIS_HOST: 'localhost',
          CFN_REDIS_PORT: '6379',
          NODE_ENV: 'test',
        }
      );

      expect(result.stdout).toContain('Spawning agent: tester');
    }, testTimeout);

    test('validates ANTHROPIC_API_KEY format', async () => {
      const result = await spawnCLI(
        ['coder'],
        {
          ANTHROPIC_API_KEY: 'invalid-format!@#$',
        }
      );

      expect(result.stdout).toContain('ANTHROPIC_API_KEY format invalid');
    }, testTimeout);

    test('accepts valid ANTHROPIC_API_KEY format', async () => {
      const result = await spawnCLI(
        ['researcher'],
        {
          ANTHROPIC_API_KEY: 'sk-ant-test-key-valid',
        }
      );

      // Should not show validation warning
      expect(result.stdout).not.toContain('ANTHROPIC_API_KEY format invalid');
    }, testTimeout);
  });

  // ============================================================================
  // Redis Context Injection Tests
  // ============================================================================

  describe('Redis Context Injection', () => {
    test('skips Redis fetch when no task ID provided', async () => {
      const result = await spawnCLI(['developer']);

      expect(result.stdout).toContain('Spawning agent: developer');
      // Should not log epic context loading
      expect(result.stdout).not.toContain('Epic context loaded from Redis');
    }, testTimeout);

    test('attempts Redis fetch with task ID', async () => {
      const result = await spawnCLI(['implementer', '--task-id', 'epic-123']);

      // Should attempt spawn (Redis might fail but that's OK)
      expect(result.stdout).toContain('Spawning agent: implementer');
      expect(result.stdout).toContain('Task ID: epic-123');
    }, testTimeout);

    test('handles Redis unavailability gracefully', async () => {
      const result = await spawnCLI(
        ['validator', '--task-id', 'task-fail'],
        {
          CFN_REDIS_HOST: 'nonexistent-host',
          CFN_REDIS_PORT: '9999',
        }
      );

      // Should continue despite Redis failure
      expect(result.stdout).toContain('Spawning agent: validator');
    }, testTimeout);
  });

  // ============================================================================
  // Edge Cases and Error Scenarios
  // ============================================================================

  describe('Edge Cases and Error Scenarios', () => {
    test('handles very long context strings', async () => {
      const longContext = 'A'.repeat(500);
      const result = await spawnCLI(['coder', '--context', longContext]);

      expect(result.stdout).toContain('Spawning agent: coder');
      expect(result.stdout).toContain('Context: A');
    }, testTimeout);

    test('handles iteration value of 0', async () => {
      const result = await spawnCLI(['reviewer', '--iteration', '0']);

      expect(result.stdout).toContain('Iteration: 0');
    }, testTimeout);

    test('handles negative priority value', async () => {
      const result = await spawnCLI(['tester', '--priority', '-5']);

      expect(result.stdout).toContain('--priority -5');
    }, testTimeout);

    test('handles multiple unknown options', async () => {
      const result = await spawnCLI([
        'researcher',
        '--unknown1', 'value1',
        '--unknown2', 'value2',
        '--task-id', 'task-123',
      ]);

      expect(result.stdout).toContain('Unknown option: --unknown1');
      expect(result.stdout).toContain('Unknown option: --unknown2');
      // Should continue despite warnings
      expect(result.stdout).toContain('Spawning agent: researcher');
    }, testTimeout);

    test('handles empty string values for parameters', async () => {
      const result = await spawnCLI(['coder', '--context', '', '--task-id', '']);

      expect(result.stdout).toContain('Spawning agent: coder');
    }, testTimeout);

    test('handles malformed iteration value', async () => {
      const result = await spawnCLI(['validator', '--iteration', 'not-a-number']);

      // Should parse as NaN but continue
      expect(result.stdout).toContain('Spawning agent: validator');
    }, testTimeout);

    test('handles context with special characters', async () => {
      const result = await spawnCLI(['backend-developer', '--context', 'Fix bug #123 @priority']);

      expect(result.stdout).toContain('Context: Fix bug #123 @priority');
    }, testTimeout);

    test('handles hyphenated agent types', async () => {
      const result = await spawnCLI(['quality-assurance', '--task-id', 'qa-001']);

      expect(result.stdout).toContain('Spawning agent: quality-assurance');
    }, testTimeout);

    test('handles numeric task IDs', async () => {
      const result = await spawnCLI(['developer', '--task-id', '12345']);

      expect(result.stdout).toContain('Task ID: 12345');
    }, testTimeout);

    test('handles mixed case agent types', async () => {
      const result = await spawnCLI(['BackendDeveloper']);

      expect(result.stdout).toContain('Spawning agent: BackendDeveloper');
    }, testTimeout);
  });

  // ============================================================================
  // Integration Tests
  // ============================================================================

  describe('Integration Tests', () => {
    test('complete spawn cycle with all parameters', async () => {
      const result = await spawnCLI(
        [
          'agent',
          'backend-developer',
          '--agent-id', 'dev-001',
          '--task-id', 'auth-epic',
          '--iteration', '3',
          '--context', 'JWT implementation',
          '--mode', 'api',
          '--priority', '9',
          '--parent-task-id', 'parent-epic',
        ],
        {
          CFN_REDIS_HOST: 'localhost',
          CFN_REDIS_PORT: '6379',
          NODE_ENV: 'production',
        }
      );

      expect(result.stdout).toContain('Spawning agent: backend-developer');
      expect(result.stdout).toContain('Agent ID: dev-001');
      expect(result.stdout).toContain('Task ID: auth-epic');
      expect(result.stdout).toContain('Iteration: 3');
      expect(result.stdout).toContain('Context: JWT implementation');
      expect(result.stdout).toContain('Mode: api');
      expect(result.stdout).toContain('Executing: npx');
      expect(result.stdout).toContain('--agent-id dev-001');
      expect(result.stdout).toContain('--task-id auth-epic');
      expect(result.stdout).toContain('--iteration 3');
      expect(result.stdout).toContain('--mode api');
      expect(result.stdout).toContain('--priority 9');
      expect(result.stdout).toContain('--parent-task-id parent-epic');
    }, testTimeout);

    test('minimal spawn with agent type only', async () => {
      const result = await spawnCLI(['researcher']);

      expect(result.stdout).toContain('Spawning agent: researcher');
      expect(result.stdout).toContain('Executing: npx claude-flow-novice agent researcher');
    }, testTimeout);

    test('spawn with task context', async () => {
      const result = await spawnCLI([
        'tester',
        '--task-id', 'test-phase',
        '--iteration', '1',
        '--context', 'Run integration tests',
      ]);

      expect(result.stdout).toContain('Spawning agent: tester');
      expect(result.stdout).toContain('Task ID: test-phase');
      expect(result.stdout).toContain('Iteration: 1');
      expect(result.stdout).toContain('Context: Run integration tests');
    }, testTimeout);

    test('spawn with mode and priority', async () => {
      const result = await spawnCLI([
        'validator',
        '--mode', 'hybrid',
        '--priority', '10',
      ]);

      expect(result.stdout).toContain('Spawning agent: validator');
      expect(result.stdout).toContain('Mode: hybrid');
      expect(result.stdout).toContain('--mode hybrid');
      expect(result.stdout).toContain('--priority 10');
    }, testTimeout);

    test('spawn with parent task relationship', async () => {
      const result = await spawnCLI([
        'frontend-designer',
        '--task-id', 'child-task-001',
        '--parent-task-id', 'epic-ui-redesign',
      ]);

      expect(result.stdout).toContain('Spawning agent: frontend-designer');
      expect(result.stdout).toContain('Task ID: child-task-001');
      expect(result.stdout).toContain('--parent-task-id epic-ui-redesign');
    }, testTimeout);
  });

  // ============================================================================
  // Logging Tests
  // ============================================================================

  describe('Logging and Output', () => {
    test('logs spawn information for basic invocation', async () => {
      const result = await spawnCLI(['researcher']);

      expect(result.stdout).toContain('[cfn-spawn] Spawning agent: researcher');
      expect(result.stdout).toContain('[cfn-spawn] Executing: npx');
    }, testTimeout);

    test('logs all provided parameters', async () => {
      const result = await spawnCLI([
        'backend-developer',
        '--agent-id', 'agent-123',
        '--task-id', 'task-456',
        '--iteration', '2',
        '--context', 'Implement auth',
        '--mode', 'hybrid',
      ]);

      expect(result.stdout).toContain('[cfn-spawn]   Agent ID: agent-123');
      expect(result.stdout).toContain('[cfn-spawn]   Task ID: task-456');
      expect(result.stdout).toContain('[cfn-spawn]   Iteration: 2');
      expect(result.stdout).toContain('[cfn-spawn]   Context: Implement auth');
      expect(result.stdout).toContain('[cfn-spawn]   Mode: hybrid');
    }, testTimeout);

    test('logs only provided parameters (sparse output)', async () => {
      const result = await spawnCLI(['tester', '--task-id', 'task-789']);

      expect(result.stdout).toContain('Task ID: task-789');
      expect(result.stdout).not.toContain('Agent ID:'); // Not provided
      expect(result.stdout).not.toContain('Iteration:'); // Not provided (uses default)
    }, testTimeout);
  });
});
