/**
 * Introspection Panel - Shows discovered expressions and extractors from NX model
 */

import { useState, useEffect, useCallback, useMemo } from 'react';
import {
  X,
  Search,
  RefreshCw,
  Plus,
  ChevronDown,
  ChevronRight,
  FileBox,
  Cpu,
  FlaskConical,
  SlidersHorizontal,
  AlertTriangle,
} from 'lucide-react';
import { useCanvasStore } from '../../../hooks/useCanvasStore';

interface IntrospectionPanelProps {
  filePath: string;
  studyId?: string;
  onClose: () => void;
}

interface Expression {
  name: string;
  value: number;
  rhs?: string;
  min?: number;
  max?: number;
  unit?: string;
  units?: string;  // API returns 'units' not 'unit'
  type: string;
  source?: string;
}

interface Extractor {
  id: string;
  name: string;
  description?: string;
  always?: boolean;
}

interface DependentFile {
  path: string;
  type: string;
  name: string;
}

// The API returns expressions in a nested structure
interface ExpressionsResult {
  user: Expression[];
  internal: Expression[];
  total_count: number;
  user_count: number;
}

interface IntrospectionResult {
  part_file?: string;
  part_path?: string;
  file_path?: string;
  file_type?: string;
  success?: boolean;
  error?: string | null;
  // Expressions can be either an array (old format) or object with user/internal (new format)
  expressions: Expression[] | ExpressionsResult;
  solver_type?: string | null;
  dependent_files?: DependentFile[];
  extractors_available?: Extractor[];
  warnings?: string[];
  // Additional fields from NX introspection
  mass_properties?: Record<string, unknown>;
  materials?: Record<string, unknown>;
  bodies?: Record<string, unknown>;
  attributes?: Array<{ title: string; value: string }>;
  units?: Record<string, unknown>;
  linked_parts?: Record<string, unknown>;
}

export function IntrospectionPanel({ filePath, studyId, onClose }: IntrospectionPanelProps) {
  const [result, setResult] = useState<IntrospectionResult | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [expandedSections, setExpandedSections] = useState<Set<string>>(
    new Set(['expressions', 'extractors'])
  );
  const [searchTerm, setSearchTerm] = useState('');

  const { addNode, nodes } = useCanvasStore();

  const runIntrospection = useCallback(async () => {
    if (!filePath) return;

    setIsLoading(true);
    setError(null);
    try {
      let res;
      
      // If we have a studyId, use the study-aware introspection endpoint
      if (studyId) {
        // Don't encode studyId - it may contain slashes for nested paths (e.g., M1_Mirror/study_name)
        res = await fetch(`/api/optimization/studies/${studyId}/nx/introspect`);
      } else {
        // Fallback to direct path introspection
        res = await fetch('/api/nx/introspect', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ file_path: filePath }),
        });
      }
      
      if (!res.ok) {
        const errData = await res.json().catch(() => ({}));
        throw new Error(errData.detail || 'Introspection failed');
      }
      const data = await res.json();
      
      // Handle different response formats
      setResult(data.introspection || data);
    } catch (e) {
      const msg = e instanceof Error ? e.message : 'Failed to introspect model';
      setError(msg);
      console.error('Introspection error:', e);
    } finally {
      setIsLoading(false);
    }
  }, [filePath, studyId]);

  useEffect(() => {
    runIntrospection();
  }, [runIntrospection]);

  const toggleSection = (section: string) => {
    setExpandedSections((prev) => {
      const next = new Set(prev);
      if (next.has(section)) next.delete(section);
      else next.add(section);
      return next;
    });
  };

  const addExpressionAsDesignVar = (expr: Expression) => {
    // Find a good position (left of model node)
    const modelNode = nodes.find((n) => n.data.type === 'model');
    const existingDvars = nodes.filter((n) => n.data.type === 'designVar');

    const position = {
      x: (modelNode?.position.x || 300) - 250,
      y: (modelNode?.position.y || 100) + existingDvars.length * 100,
    };

    // Calculate min/max based on value if not provided
    const minValue = expr.min ?? expr.value * 0.5;
    const maxValue = expr.max ?? expr.value * 1.5;

    addNode('designVar', position, {
      label: expr.name,
      expressionName: expr.name,
      minValue,
      maxValue,
      unit: expr.unit,
      configured: true,
    });
  };

  const addExtractorNode = (extractor: Extractor) => {
    // Find a good position (right of solver node)
    const solverNode = nodes.find((n) => n.data.type === 'solver');
    const existingExtractors = nodes.filter((n) => n.data.type === 'extractor');

    const position = {
      x: (solverNode?.position.x || 400) + 200,
      y: (solverNode?.position.y || 100) + existingExtractors.length * 100,
    };

    addNode('extractor', position, {
      label: extractor.name,
      extractorId: extractor.id,
      extractorName: extractor.name,
      configured: true,
    });
  };

  // Handle both array format (old) and object format (new API)
  const allExpressions: Expression[] = useMemo(() => {
    if (!result?.expressions) return [];
    
    // Check if expressions is an array (old format) or object (new format)
    if (Array.isArray(result.expressions)) {
      return result.expressions;
    }
    
    // New format: { user: [...], internal: [...] }
    const exprObj = result.expressions as ExpressionsResult;
    return [...(exprObj.user || []), ...(exprObj.internal || [])];
  }, [result?.expressions]);

  const filteredExpressions = allExpressions.filter((e) =>
    e.name.toLowerCase().includes(searchTerm.toLowerCase())
  );

  return (
    <div className="bg-dark-850 border border-dark-700 rounded-xl w-80 max-h-[70vh] flex flex-col shadow-xl">
      {/* Header */}
      <div className="flex items-center justify-between px-4 py-3 border-b border-dark-700">
        <div className="flex items-center gap-2">
          <Search size={16} className="text-primary-400" />
          <span className="font-medium text-white text-sm">Model Introspection</span>
        </div>
        <div className="flex items-center gap-1">
          <button
            onClick={runIntrospection}
            disabled={isLoading}
            className="p-1.5 text-dark-400 hover:text-white hover:bg-dark-700 rounded transition-colors"
            title="Refresh"
          >
            <RefreshCw size={14} className={isLoading ? 'animate-spin' : ''} />
          </button>
          <button
            onClick={onClose}
            className="p-1.5 text-dark-400 hover:text-white hover:bg-dark-700 rounded transition-colors"
          >
            <X size={14} />
          </button>
        </div>
      </div>

      {/* Search */}
      <div className="px-4 py-2 border-b border-dark-700">
        <input
          type="text"
          placeholder="Filter expressions..."
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
          className="w-full px-3 py-1.5 bg-dark-800 border border-dark-600 rounded-lg
                     text-sm text-white placeholder-dark-500 focus:outline-none focus:border-primary-500"
        />
      </div>

      {/* Content */}
      <div className="flex-1 overflow-auto">
        {isLoading ? (
          <div className="flex items-center justify-center h-32 text-dark-500">
            <RefreshCw size={20} className="animate-spin mr-2" />
            Analyzing model...
          </div>
        ) : error ? (
          <div className="p-4 text-red-400 text-sm">{error}</div>
        ) : result ? (
          <div className="p-2 space-y-2">
            {/* Solver Type */}
            {result.solver_type && (
              <div className="p-2 bg-dark-800 rounded-lg">
                <div className="flex items-center gap-2 text-sm">
                  <Cpu size={14} className="text-violet-400" />
                  <span className="text-dark-300">Solver:</span>
                  <span className="text-white font-medium">{result.solver_type}</span>
                </div>
              </div>
            )}

            {/* Expressions Section */}
            <div className="border border-dark-700 rounded-lg overflow-hidden">
              <button
                onClick={() => toggleSection('expressions')}
                className="w-full flex items-center justify-between px-3 py-2 bg-dark-800 hover:bg-dark-750 transition-colors"
              >
                <div className="flex items-center gap-2">
                  <SlidersHorizontal size={14} className="text-emerald-400" />
                  <span className="text-sm font-medium text-white">
                    Expressions ({filteredExpressions.length})
                  </span>
                </div>
                {expandedSections.has('expressions') ? (
                  <ChevronDown size={14} className="text-dark-400" />
                ) : (
                  <ChevronRight size={14} className="text-dark-400" />
                )}
              </button>

              {expandedSections.has('expressions') && (
                <div className="p-2 space-y-1 max-h-48 overflow-y-auto">
                  {filteredExpressions.length === 0 ? (
                    <p className="text-xs text-dark-500 text-center py-2">
                      No expressions found
                    </p>
                  ) : (
                    filteredExpressions.map((expr) => (
                      <div
                        key={expr.name}
                        className="flex items-center justify-between p-2 bg-dark-850 rounded hover:bg-dark-750 group transition-colors"
                      >
                        <div className="flex-1 min-w-0">
                          <p className="text-sm text-white truncate">{expr.name}</p>
                          <p className="text-xs text-dark-500">
                            {expr.value} {expr.units || expr.unit || ''}
                            {expr.source === 'inferred' && (
                              <span className="ml-1 text-amber-500">(inferred)</span>
                            )}
                          </p>
                        </div>
                        <button
                          onClick={() => addExpressionAsDesignVar(expr)}
                          className="p-1.5 text-dark-500 hover:text-primary-400 hover:bg-dark-700 rounded
                                     opacity-0 group-hover:opacity-100 transition-all"
                          title="Add as Design Variable"
                        >
                          <Plus size={14} />
                        </button>
                      </div>
                    ))
                  )}
                </div>
              )}
            </div>

            {/* Extractors Section - only show if available */}
            {(result.extractors_available?.length ?? 0) > 0 && (
            <div className="border border-dark-700 rounded-lg overflow-hidden">
              <button
                onClick={() => toggleSection('extractors')}
                className="w-full flex items-center justify-between px-3 py-2 bg-dark-800 hover:bg-dark-750 transition-colors"
              >
                <div className="flex items-center gap-2">
                  <FlaskConical size={14} className="text-cyan-400" />
                  <span className="text-sm font-medium text-white">
                    Available Extractors ({result.extractors_available?.length || 0})
                  </span>
                </div>
                {expandedSections.has('extractors') ? (
                  <ChevronDown size={14} className="text-dark-400" />
                ) : (
                  <ChevronRight size={14} className="text-dark-400" />
                )}
              </button>

              {expandedSections.has('extractors') && (
                <div className="p-2 space-y-1 max-h-48 overflow-y-auto">
                  {(result.extractors_available || []).map((ext) => (
                    <div
                      key={ext.id}
                      className="flex items-center justify-between p-2 bg-dark-850 rounded hover:bg-dark-750 group transition-colors"
                    >
                      <div className="flex-1 min-w-0">
                        <p className="text-sm text-white">{ext.name}</p>
                        <p className="text-xs text-dark-500">
                          {ext.id}
                          {ext.description && ` - ${ext.description}`}
                        </p>
                      </div>
                      <button
                        onClick={() => addExtractorNode(ext)}
                        className="p-1.5 text-dark-500 hover:text-primary-400 hover:bg-dark-700 rounded
                                   opacity-0 group-hover:opacity-100 transition-all"
                        title="Add Extractor"
                      >
                        <Plus size={14} />
                      </button>
                    </div>
                  ))}
                </div>
              )}
            </div>
            )}

            {/* Dependent Files */}
            {(result.dependent_files?.length ?? 0) > 0 && (
              <div className="border border-dark-700 rounded-lg overflow-hidden">
                <button
                  onClick={() => toggleSection('files')}
                  className="w-full flex items-center justify-between px-3 py-2 bg-dark-800 hover:bg-dark-750 transition-colors"
                >
                  <div className="flex items-center gap-2">
                    <FileBox size={14} className="text-amber-400" />
                    <span className="text-sm font-medium text-white">
                      Dependent Files ({result.dependent_files?.length || 0})
                    </span>
                  </div>
                  {expandedSections.has('files') ? (
                    <ChevronDown size={14} className="text-dark-400" />
                  ) : (
                    <ChevronRight size={14} className="text-dark-400" />
                  )}
                </button>

                {expandedSections.has('files') && (
                  <div className="p-2 space-y-1 max-h-32 overflow-y-auto">
                    {(result.dependent_files || []).map((file) => (
                      <div
                        key={file.path}
                        className="flex items-center gap-2 p-2 bg-dark-850 rounded"
                      >
                        <FileBox size={14} className="text-dark-400 flex-shrink-0" />
                        <div className="flex-1 min-w-0">
                          <p className="text-sm text-white truncate">{file.name}</p>
                          <p className="text-xs text-dark-500">{file.type}</p>
                        </div>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            )}

            {/* Warnings */}
            {(result.warnings?.length ?? 0) > 0 && (
              <div className="p-2 bg-amber-500/10 border border-amber-500/30 rounded-lg">
                <div className="flex items-center gap-1.5 mb-1">
                  <AlertTriangle size={12} className="text-amber-400" />
                  <p className="text-xs text-amber-400 font-medium">Warnings</p>
                </div>
                {(result.warnings || []).map((w, i) => (
                  <p key={i} className="text-xs text-amber-300">
                    {w}
                  </p>
                ))}
              </div>
            )}
          </div>
        ) : (
          <div className="flex items-center justify-center h-32 text-dark-500 text-sm">
            Select a model to introspect
          </div>
        )}
      </div>
    </div>
  );
}
