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

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

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

interface CodeEditorPanelProps {
  /** Initial code content */
  initialCode?: string;
  /** Callback when code changes */
  onChange?: (code: string) => void;
  /** Callback when user requests Claude generation */
  onRequestGeneration?: (prompt: string) => Promise<string>;
  /** 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> }>;
  /** Close button callback */
  onClose?: () => void;
}

// 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
    }
`;

export function CodeEditorPanel({
  initialCode = DEFAULT_EXTRACTOR_TEMPLATE,
  onChange,
  onRequestGeneration,
  readOnly = false,
  extractorName = 'custom_extractor',
  outputs = [],
  height = 400,
  showHeader = true,
  onRun,
  onClose,
}: CodeEditorPanelProps) {
  const [code, setCode] = useState(initialCode);
  const [isGenerating, setIsGenerating] = useState(false);
  const [isRunning, setIsRunning] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [runResult, setRunResult] = useState<{ success: boolean; outputs?: Record<string, unknown> } | null>(null);
  const [copied, setCopied] = useState(false);
  const [showPromptInput, setShowPromptInput] = useState(false);
  const [generationPrompt, setGenerationPrompt] = useState('');
  const [showOutputs, setShowOutputs] = useState(true);

  const editorRef = useRef<EditorInstance | null>(null);
  const monacoRef = useRef<Monaco | null>(null);

  // 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
  const handleGenerate = useCallback(async () => {
    if (!onRequestGeneration || !generationPrompt.trim()) return;

    setIsGenerating(true);
    setError(null);

    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, generationPrompt, onChange]);

  // 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]);

  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">
            {/* Claude Generate Button */}
            {onRequestGeneration && (
              <button
                onClick={() => setShowPromptInput(!showPromptInput)}
                className="p-1.5 rounded text-violet-400 hover:bg-violet-500/20 transition-colors"
                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}
                className="p-1.5 rounded text-emerald-400 hover:bg-emerald-500/20 transition-colors disabled:opacity-50"
                title="Validate code"
              >
                {isRunning ? (
                  <RefreshCw size={16} className="animate-spin" />
                ) : (
                  <Play 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)}
              className="px-3 py-1.5 text-xs text-dark-400 hover:text-white transition-colors"
            >
              Cancel
            </button>
            <button
              onClick={handleGenerate}
              disabled={isGenerating || !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"
            >
              {isGenerating ? (
                <>
                  <RefreshCw size={12} className="animate-spin" />
                  Generating...
                </>
              ) : (
                <>
                  <Sparkles size={12} />
                  Generate
                </>
              )}
            </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>

      {/* 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>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;
