/**
 * CodeEditorPanel - Monaco editor for custom extractor Python code
 *
 * Features:
 * - Python syntax highlighting
 * - Auto-completion for common patterns
 * - Error display
 * - Claude AI code generation with streaming support
 * - Preview of extracted outputs
 * - Code snippets library
 */

import { useState, useCallback, useRef, useEffect } from 'react';
import Editor, { OnMount, OnChange } from '@monaco-editor/react';
import {
  Play,
  Wand2,
  Copy,
  Check,
  AlertCircle,
  RefreshCw,
  X,
  ChevronDown,
  ChevronRight,
  FileCode,
  Sparkles,
  Square,
  BookOpen,
  FlaskConical,
} from 'lucide-react';

// Monaco editor types
type Monaco = Parameters<OnMount>[1];
type EditorInstance = Parameters<OnMount>[0];

/** Streaming generation callbacks */
export interface StreamingCallbacks {
  onToken: (token: string) => void;
  onComplete: (code: string, outputs: string[]) => void;
  onError: (error: string) => void;
}

/** Request format for streaming generation */
export interface StreamingGenerationRequest {
  prompt: string;
  study_id?: string;
  existing_code?: string;
  output_names?: string[];
}

interface CodeEditorPanelProps {
  /** Initial code content */
  initialCode?: string;
  /** Callback when code changes */
  onChange?: (code: string) => void;
  /** Callback when user requests Claude generation (non-streaming) */
  onRequestGeneration?: (prompt: string) => Promise<string>;
  /** Callback for streaming generation (preferred over onRequestGeneration) */
  onRequestStreamingGeneration?: (
    request: StreamingGenerationRequest,
    callbacks: StreamingCallbacks
  ) => AbortController;
  /** Whether the panel is read-only */
  readOnly?: boolean;
  /** Extractor name for context */
  extractorName?: string;
  /** Output variable names */
  outputs?: string[];
  /** Optional height (default: 300px) */
  height?: number | string;
  /** Show/hide header */
  showHeader?: boolean;
  /** Callback when running code (validation) */
  onRun?: (code: string) => Promise<{ success: boolean; error?: string; outputs?: Record<string, unknown> }>;
  /** Callback for live testing against OP2 file */
  onTest?: (code: string) => Promise<{ success: boolean; error?: string; outputs?: Record<string, number>; execution_time_ms?: number }>;
  /** Close button callback */
  onClose?: () => void;
  /** Study ID for context in generation */
  studyId?: string;
}

// Default Python template for custom extractors
const DEFAULT_EXTRACTOR_TEMPLATE = `"""
Custom Extractor Function

This function is called after FEA simulation completes.
It receives the results and should return extracted values.

Available inputs:
- op2_path: Path to the .op2 results file
- fem_path: Path to the .fem file  
- params: Dict of current design variable values
- subcase_id: Current subcase being analyzed (optional)

Return a dict with your extracted values.
"""

from pyNastran.op2.op2 import OP2
import numpy as np

def extract(op2_path: str, fem_path: str, params: dict, subcase_id: int = 1) -> dict:
    """
    Extract physics from FEA results.
    
    Args:
        op2_path: Path to OP2 results file
        fem_path: Path to FEM file
        params: Current design variable values
        subcase_id: Subcase ID to analyze
        
    Returns:
        Dict with extracted values, e.g. {'max_stress': 150.5, 'mass': 2.3}
    """
    # Load OP2 results
    op2 = OP2()
    op2.read_op2(op2_path)
    
    # Example: Extract max displacement
    if subcase_id in op2.displacements:
        disp = op2.displacements[subcase_id]
        # Get magnitude of displacement vectors
        magnitudes = np.sqrt(np.sum(disp.data[0, :, 1:4]**2, axis=1))
        max_disp = float(np.max(magnitudes))
    else:
        max_disp = 0.0
    
    return {
        'max_displacement': max_disp,
        # Add more outputs as needed
    }
`;

// Code snippets library
interface CodeSnippet {
  id: string;
  name: string;
  category: string;
  description: string;
  code: string;
}

const CODE_SNIPPETS: CodeSnippet[] = [
  {
    id: 'displacement',
    name: 'Max Displacement',
    category: 'Displacement',
    description: 'Extract maximum displacement magnitude from results',
    code: `"""Extract maximum displacement magnitude"""

from pyNastran.op2.op2 import OP2
import numpy as np

def extract(op2_path: str, fem_path: str, params: dict, subcase_id: int = 1) -> dict:
    op2 = OP2()
    op2.read_op2(op2_path)
    
    if subcase_id in op2.displacements:
        disp = op2.displacements[subcase_id]
        # Displacement data: [time, node, component] where component 1-3 are x,y,z
        magnitudes = np.sqrt(np.sum(disp.data[0, :, 1:4]**2, axis=1))
        max_disp = float(np.max(magnitudes))
    else:
        max_disp = 0.0
    
    return {'max_displacement': max_disp}
`,
  },
  {
    id: 'stress_vonmises',
    name: 'Von Mises Stress',
    category: 'Stress',
    description: 'Extract maximum von Mises stress from shell elements',
    code: `"""Extract maximum von Mises stress from shell elements"""

from pyNastran.op2.op2 import OP2
import numpy as np

def extract(op2_path: str, fem_path: str, params: dict, subcase_id: int = 1) -> dict:
    op2 = OP2()
    op2.read_op2(op2_path)
    
    max_stress = 0.0
    
    # Check CQUAD4 elements
    if subcase_id in op2.cquad4_stress:
        stress = op2.cquad4_stress[subcase_id]
        # Von Mises is typically in the last column
        vm_stress = stress.data[0, :, -1]  # [time, element, component]
        max_stress = max(max_stress, float(np.max(np.abs(vm_stress))))
    
    # Check CTRIA3 elements
    if subcase_id in op2.ctria3_stress:
        stress = op2.ctria3_stress[subcase_id]
        vm_stress = stress.data[0, :, -1]
        max_stress = max(max_stress, float(np.max(np.abs(vm_stress))))
    
    return {'max_vonmises': max_stress}
`,
  },
  {
    id: 'frequency',
    name: 'Natural Frequency',
    category: 'Modal',
    description: 'Extract first natural frequency from modal analysis',
    code: `"""Extract natural frequencies from modal analysis"""

from pyNastran.op2.op2 import OP2

def extract(op2_path: str, fem_path: str, params: dict, subcase_id: int = 1) -> dict:
    op2 = OP2()
    op2.read_op2(op2_path)
    
    freq_1 = 0.0
    freq_2 = 0.0
    freq_3 = 0.0
    
    if subcase_id in op2.eigenvalues:
        eig = op2.eigenvalues[subcase_id]
        freqs = eig.radians / (2 * 3.14159)  # Convert to Hz
        if len(freqs) >= 1:
            freq_1 = float(freqs[0])
        if len(freqs) >= 2:
            freq_2 = float(freqs[1])
        if len(freqs) >= 3:
            freq_3 = float(freqs[2])
    
    return {
        'freq_1': freq_1,
        'freq_2': freq_2,
        'freq_3': freq_3,
    }
`,
  },
  {
    id: 'mass_grid',
    name: 'Grid Point Mass',
    category: 'Mass',
    description: 'Extract total mass from grid point weight generator',
    code: `"""Extract mass from grid point weight generator"""

from pyNastran.op2.op2 import OP2

def extract(op2_path: str, fem_path: str, params: dict, subcase_id: int = 1) -> dict:
    op2 = OP2()
    op2.read_op2(op2_path)
    
    total_mass = 0.0
    
    if hasattr(op2, 'grid_point_weight') and op2.grid_point_weight:
        gpw = op2.grid_point_weight
        # Mass is typically M[0,0] in the mass matrix
        if hasattr(gpw, 'mass') and len(gpw.mass) > 0:
            total_mass = float(gpw.mass[0])
    
    return {'total_mass': total_mass}
`,
  },
  {
    id: 'strain_energy',
    name: 'Strain Energy',
    category: 'Energy',
    description: 'Extract total strain energy from elements',
    code: `"""Extract strain energy from elements"""

from pyNastran.op2.op2 import OP2
import numpy as np

def extract(op2_path: str, fem_path: str, params: dict, subcase_id: int = 1) -> dict:
    op2 = OP2()
    op2.read_op2(op2_path)
    
    total_energy = 0.0
    
    # Sum strain energy from all element types
    for key in dir(op2):
        if 'strain_energy' in key.lower():
            result = getattr(op2, key)
            if isinstance(result, dict) and subcase_id in result:
                se = result[subcase_id]
                if hasattr(se, 'data'):
                    total_energy += float(np.sum(se.data))
    
    return {'strain_energy': total_energy}
`,
  },
  {
    id: 'reaction_force',
    name: 'Reaction Forces',
    category: 'Force',
    description: 'Extract reaction forces at constrained nodes',
    code: `"""Extract reaction forces at single point constraints"""

from pyNastran.op2.op2 import OP2
import numpy as np

def extract(op2_path: str, fem_path: str, params: dict, subcase_id: int = 1) -> dict:
    op2 = OP2()
    op2.read_op2(op2_path)
    
    max_reaction = 0.0
    total_reaction_z = 0.0
    
    if subcase_id in op2.spc_forces:
        spc = op2.spc_forces[subcase_id]
        # SPC forces: [time, node, component] where 1-3 are Fx,Fy,Fz
        forces = spc.data[0, :, 1:4]  # Get Fx, Fy, Fz
        magnitudes = np.sqrt(np.sum(forces**2, axis=1))
        max_reaction = float(np.max(magnitudes))
        total_reaction_z = float(np.sum(forces[:, 2]))  # Sum of Fz
    
    return {
        'max_reaction': max_reaction,
        'total_reaction_z': total_reaction_z,
    }
`,
  },
];

export function CodeEditorPanel({
  initialCode = DEFAULT_EXTRACTOR_TEMPLATE,
  onChange,
  onRequestGeneration,
  onRequestStreamingGeneration,
  readOnly = false,
  extractorName = 'custom_extractor',
  outputs = [],
  height = 400,
  showHeader = true,
  onRun,
  onTest,
  onClose,
  studyId,
}: CodeEditorPanelProps) {
  const [code, setCode] = useState(initialCode);
  const [streamingCode, setStreamingCode] = useState(''); // Partial code during streaming
  const [isGenerating, setIsGenerating] = useState(false);
  const [isRunning, setIsRunning] = useState(false);
  const [isTesting, setIsTesting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [runResult, setRunResult] = useState<{ success: boolean; outputs?: Record<string, unknown> } | null>(null);
  const [testResult, setTestResult] = useState<{ success: boolean; outputs?: Record<string, number>; execution_time_ms?: number } | null>(null);
  const [copied, setCopied] = useState(false);
  const [showPromptInput, setShowPromptInput] = useState(false);
  const [generationPrompt, setGenerationPrompt] = useState('');
  const [showOutputs, setShowOutputs] = useState(true);
  const [showSnippets, setShowSnippets] = useState(false);

  const editorRef = useRef<EditorInstance | null>(null);
  const monacoRef = useRef<Monaco | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);
  
  // Cleanup abort controller on unmount
  useEffect(() => {
    return () => {
      abortControllerRef.current?.abort();
    };
  }, []);

  // Handle editor mount
  const handleEditorMount: OnMount = (editor, monaco) => {
    editorRef.current = editor;
    monacoRef.current = monaco;

    // Configure Python language
    monaco.languages.registerCompletionItemProvider('python', {
      provideCompletionItems: (model: Parameters<typeof monaco.editor.createModel>[0], position: { lineNumber: number; column: number }) => {
        const word = model.getWordUntilPosition(position);
        const range = {
          startLineNumber: position.lineNumber,
          endLineNumber: position.lineNumber,
          startColumn: word.startColumn,
          endColumn: word.endColumn,
        };

        const suggestions = [
          {
            label: 'op2.read_op2',
            kind: monaco.languages.CompletionItemKind.Method,
            insertText: 'op2.read_op2(op2_path)',
            documentation: 'Read OP2 results file',
            range,
          },
          {
            label: 'op2.displacements',
            kind: monaco.languages.CompletionItemKind.Property,
            insertText: 'op2.displacements[subcase_id]',
            documentation: 'Access displacement results for a subcase',
            range,
          },
          {
            label: 'op2.eigenvectors',
            kind: monaco.languages.CompletionItemKind.Property,
            insertText: 'op2.eigenvectors[subcase_id]',
            documentation: 'Access eigenvector results for modal analysis',
            range,
          },
          {
            label: 'np.max',
            kind: monaco.languages.CompletionItemKind.Function,
            insertText: 'np.max(${1:array})',
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            documentation: 'Get maximum value from array',
            range,
          },
          {
            label: 'np.sqrt',
            kind: monaco.languages.CompletionItemKind.Function,
            insertText: 'np.sqrt(${1:array})',
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            documentation: 'Square root of array elements',
            range,
          },
          {
            label: 'extract_function',
            kind: monaco.languages.CompletionItemKind.Snippet,
            insertText: `def extract(op2_path: str, fem_path: str, params: dict, subcase_id: int = 1) -> dict:
    """Extract physics from FEA results."""
    op2 = OP2()
    op2.read_op2(op2_path)
    
    # Your extraction logic here
    
    return {
        '\${1:output_name}': \${2:value},
    }`,
            insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            documentation: 'Insert a complete extract function template',
            range,
          },
        ];

        return { suggestions };
      },
    });
  };

  // Handle code change
  const handleCodeChange: OnChange = (value) => {
    const newCode = value || '';
    setCode(newCode);
    setError(null);
    setRunResult(null);
    onChange?.(newCode);
  };

  // Copy code to clipboard
  const handleCopy = useCallback(() => {
    navigator.clipboard.writeText(code);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  }, [code]);

  // Request Claude generation (with streaming support)
  const handleGenerate = useCallback(async () => {
    if ((!onRequestGeneration && !onRequestStreamingGeneration) || !generationPrompt.trim()) return;

    setIsGenerating(true);
    setError(null);
    setStreamingCode('');

    // Prefer streaming if available
    if (onRequestStreamingGeneration) {
      abortControllerRef.current = onRequestStreamingGeneration(
        {
          prompt: generationPrompt,
          study_id: studyId,
          existing_code: code !== DEFAULT_EXTRACTOR_TEMPLATE ? code : undefined,
          output_names: outputs,
        },
        {
          onToken: (token) => {
            setStreamingCode(prev => prev + token);
          },
          onComplete: (generatedCode, _outputs) => {
            setCode(generatedCode);
            setStreamingCode('');
            onChange?.(generatedCode);
            setShowPromptInput(false);
            setGenerationPrompt('');
            setIsGenerating(false);
          },
          onError: (errorMsg) => {
            setError(errorMsg);
            setStreamingCode('');
            setIsGenerating(false);
          },
        }
      );
    } else if (onRequestGeneration) {
      // Fallback to non-streaming
      try {
        const generatedCode = await onRequestGeneration(generationPrompt);
        setCode(generatedCode);
        onChange?.(generatedCode);
        setShowPromptInput(false);
        setGenerationPrompt('');
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Generation failed');
      } finally {
        setIsGenerating(false);
      }
    }
  }, [onRequestGeneration, onRequestStreamingGeneration, generationPrompt, onChange, code, outputs, studyId]);
  
  // Cancel ongoing generation
  const handleCancelGeneration = useCallback(() => {
    abortControllerRef.current?.abort();
    abortControllerRef.current = null;
    setIsGenerating(false);
    setStreamingCode('');
  }, []);

  // Run/validate code
  const handleRun = useCallback(async () => {
    if (!onRun) return;

    setIsRunning(true);
    setError(null);
    setRunResult(null);

    try {
      const result = await onRun(code);
      setRunResult(result);
      if (!result.success && result.error) {
        setError(result.error);
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Validation failed');
    } finally {
      setIsRunning(false);
    }
  }, [code, onRun]);
  
  // Test code against real OP2 file
  const handleTest = useCallback(async () => {
    if (!onTest) return;

    setIsTesting(true);
    setError(null);
    setTestResult(null);

    try {
      const result = await onTest(code);
      setTestResult(result);
      if (!result.success && result.error) {
        setError(result.error);
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Test failed');
    } finally {
      setIsTesting(false);
    }
  }, [code, onTest]);

  return (
    <div className="flex flex-col h-full bg-dark-850 border-l border-dark-700">
      {/* Header */}
      {showHeader && (
        <div className="flex items-center justify-between px-4 py-2 border-b border-dark-700">
          <div className="flex items-center gap-2">
            <FileCode size={16} className="text-emerald-400" />
            <span className="font-medium text-white text-sm">{extractorName}</span>
            <span className="text-xs text-dark-500">.py</span>
          </div>
          <div className="flex items-center gap-2">
            {/* Snippets Button */}
            <button
              onClick={() => setShowSnippets(!showSnippets)}
              className={`p-1.5 rounded transition-colors ${showSnippets ? 'text-amber-400 bg-amber-500/20' : 'text-dark-400 hover:text-amber-400 hover:bg-amber-500/10'}`}
              title="Code Snippets"
            >
              <BookOpen size={16} />
            </button>
            
            {/* Claude Generate Button */}
            {(onRequestGeneration || onRequestStreamingGeneration) && (
              <button
                onClick={() => setShowPromptInput(!showPromptInput)}
                className={`p-1.5 rounded transition-colors ${showPromptInput ? 'text-violet-400 bg-violet-500/20' : 'text-violet-400 hover:bg-violet-500/20'}`}
                title="Generate with Claude"
              >
                <Sparkles size={16} />
              </button>
            )}

            {/* Copy Button */}
            <button
              onClick={handleCopy}
              className="p-1.5 rounded text-dark-400 hover:text-white hover:bg-dark-700 transition-colors"
              title="Copy code"
            >
              {copied ? <Check size={16} className="text-emerald-400" /> : <Copy size={16} />}
            </button>

            {/* Run Button */}
            {onRun && (
              <button
                onClick={handleRun}
                disabled={isRunning || isTesting}
                className="p-1.5 rounded text-emerald-400 hover:bg-emerald-500/20 transition-colors disabled:opacity-50"
                title="Validate code syntax"
              >
                {isRunning ? (
                  <RefreshCw size={16} className="animate-spin" />
                ) : (
                  <Play size={16} />
                )}
              </button>
            )}
            
            {/* Test Button - Live Preview */}
            {onTest && (
              <button
                onClick={handleTest}
                disabled={isRunning || isTesting}
                className="p-1.5 rounded text-cyan-400 hover:bg-cyan-500/20 transition-colors disabled:opacity-50"
                title="Test against real OP2 file"
              >
                {isTesting ? (
                  <RefreshCw size={16} className="animate-spin" />
                ) : (
                  <FlaskConical size={16} />
                )}
              </button>
            )}

            {/* Close Button */}
            {onClose && (
              <button
                onClick={onClose}
                className="p-1.5 rounded text-dark-400 hover:text-white hover:bg-dark-700 transition-colors"
              >
                <X size={16} />
              </button>
            )}
          </div>
        </div>
      )}

      {/* Claude Prompt Input */}
      {showPromptInput && (
        <div className="px-4 py-3 border-b border-dark-700 bg-violet-500/5">
          <div className="flex items-center gap-2 mb-2">
            <Wand2 size={14} className="text-violet-400" />
            <span className="text-xs text-violet-400 font-medium">Generate with Claude</span>
          </div>
          <textarea
            value={generationPrompt}
            onChange={(e) => setGenerationPrompt(e.target.value)}
            placeholder="Describe what you want to extract... e.g., 'Extract maximum von Mises stress and total mass from the model'"
            className="w-full px-3 py-2 bg-dark-800 border border-dark-600 rounded-lg text-sm text-white placeholder-dark-500 resize-none focus:outline-none focus:border-violet-500"
            rows={2}
          />
          <div className="flex justify-end gap-2 mt-2">
            <button
              onClick={() => setShowPromptInput(false)}
              disabled={isGenerating}
              className="px-3 py-1.5 text-xs text-dark-400 hover:text-white transition-colors disabled:opacity-50"
            >
              Cancel
            </button>
            {isGenerating ? (
              <button
                onClick={handleCancelGeneration}
                className="px-3 py-1.5 text-xs bg-red-600 text-white rounded hover:bg-red-500 transition-colors flex items-center gap-1.5"
              >
                <Square size={12} />
                Stop
              </button>
            ) : (
              <button
                onClick={handleGenerate}
                disabled={!generationPrompt.trim()}
                className="px-3 py-1.5 text-xs bg-violet-600 text-white rounded hover:bg-violet-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center gap-1.5"
              >
                <Sparkles size={12} />
                Generate
              </button>
            )}
          </div>
          
          {/* Streaming Preview */}
          {isGenerating && streamingCode && (
            <div className="mt-3 p-3 bg-dark-900 rounded-lg border border-dark-600 max-h-48 overflow-auto">
              <div className="flex items-center gap-2 mb-2">
                <RefreshCw size={12} className="text-violet-400 animate-spin" />
                <span className="text-xs text-violet-400">Generating code...</span>
              </div>
              <pre className="text-xs text-dark-300 font-mono whitespace-pre-wrap">{streamingCode}</pre>
            </div>
          )}
        </div>
      )}

      {/* Code Snippets Panel */}
      {showSnippets && (
        <div className="px-4 py-3 border-b border-dark-700 bg-amber-500/5 max-h-64 overflow-y-auto">
          <div className="flex items-center justify-between mb-3">
            <div className="flex items-center gap-2">
              <BookOpen size={14} className="text-amber-400" />
              <span className="text-xs text-amber-400 font-medium">Code Snippets</span>
            </div>
            <button
              onClick={() => setShowSnippets(false)}
              className="p-1 rounded hover:bg-dark-700 text-dark-400 hover:text-white"
            >
              <X size={14} />
            </button>
          </div>
          <div className="space-y-2">
            {CODE_SNIPPETS.map((snippet) => (
              <button
                key={snippet.id}
                onClick={() => {
                  setCode(snippet.code);
                  onChange?.(snippet.code);
                  setShowSnippets(false);
                }}
                className="w-full text-left p-2 rounded-lg bg-dark-800 hover:bg-dark-700 border border-dark-600 hover:border-amber-500/30 transition-colors group"
              >
                <div className="flex items-center gap-2 mb-1">
                  <span className="text-sm font-medium text-white group-hover:text-amber-400 transition-colors">
                    {snippet.name}
                  </span>
                  <span className="text-xs text-dark-500 bg-dark-700 px-1.5 py-0.5 rounded">
                    {snippet.category}
                  </span>
                </div>
                <p className="text-xs text-dark-400">{snippet.description}</p>
              </button>
            ))}
          </div>
        </div>
      )}

      {/* Error Display */}
      {error && (
        <div className="px-4 py-2 bg-red-500/10 border-b border-red-500/30 flex items-center gap-2">
          <AlertCircle size={14} className="text-red-400 flex-shrink-0" />
          <span className="text-xs text-red-400 font-mono">{error}</span>
        </div>
      )}

      {/* Monaco Editor */}
      <div className="flex-1 min-h-0">
        <Editor
          height={height}
          language="python"
          theme="vs-dark"
          value={code}
          onChange={handleCodeChange}
          onMount={handleEditorMount}
          options={{
            readOnly,
            minimap: { enabled: false },
            fontSize: 13,
            lineNumbers: 'on',
            scrollBeyondLastLine: false,
            wordWrap: 'on',
            automaticLayout: true,
            tabSize: 4,
            insertSpaces: true,
            padding: { top: 8, bottom: 8 },
            scrollbar: {
              vertical: 'auto',
              horizontal: 'auto',
            },
          }}
        />
      </div>

      {/* Test Results Preview */}
      {testResult && testResult.success && testResult.outputs && (
        <div className="border-t border-dark-700 bg-cyan-500/5">
          <div className="px-4 py-2 flex items-center gap-2 text-xs">
            <FlaskConical size={12} className="text-cyan-400" />
            <span className="text-cyan-400 font-medium">Live Test Results</span>
            {testResult.execution_time_ms && (
              <span className="ml-auto text-dark-500">
                {testResult.execution_time_ms.toFixed(0)}ms
              </span>
            )}
          </div>
          <div className="px-4 pb-3 space-y-1">
            {Object.entries(testResult.outputs).map(([key, value]) => (
              <div
                key={key}
                className="flex items-center justify-between px-2 py-1 bg-dark-800 rounded text-xs"
              >
                <span className="text-cyan-400 font-mono">{key}</span>
                <span className="text-white font-medium">{typeof value === 'number' ? value.toFixed(6) : String(value)}</span>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Outputs Preview */}
      {(outputs.length > 0 || runResult?.outputs) && (
        <div className="border-t border-dark-700">
          <button
            onClick={() => setShowOutputs(!showOutputs)}
            className="w-full px-4 py-2 flex items-center gap-2 text-xs text-dark-400 hover:text-white hover:bg-dark-800/50 transition-colors"
          >
            {showOutputs ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
            <span>Expected Outputs</span>
            <span className="ml-auto text-dark-500">
              {runResult?.outputs
                ? Object.keys(runResult.outputs).length
                : outputs.length}
            </span>
          </button>
          {showOutputs && (
            <div className="px-4 pb-3 space-y-1">
              {runResult?.outputs ? (
                Object.entries(runResult.outputs).map(([key, value]) => (
                  <div
                    key={key}
                    className="flex items-center justify-between px-2 py-1 bg-dark-800 rounded text-xs"
                  >
                    <span className="text-emerald-400 font-mono">{key}</span>
                    <span className="text-dark-300">{String(value)}</span>
                  </div>
                ))
              ) : (
                outputs.map((output) => (
                  <div
                    key={output}
                    className="flex items-center px-2 py-1 bg-dark-800 rounded text-xs"
                  >
                    <span className="text-dark-400 font-mono">{output}</span>
                  </div>
                ))
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

export default CodeEditorPanel;
