'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import '@xterm/xterm/css/xterm.css';
import { Button } from './ui/button';
import { Play, Square, Trash2, X, Send, Keyboard, ChevronUp, ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';

interface TerminalProps {
  scriptPath: string;
  onClose: () => void;
  mode?: 'local' | 'ssh';
  server?: any;
  isUpdate?: boolean;
  isShell?: boolean;
  isBackup?: boolean;
  isClone?: boolean;
  containerId?: string;
  storage?: string;
  backupStorage?: string;
  executionId?: string;
  cloneCount?: number;
  hostnames?: string[];
  containerType?: 'lxc' | 'vm';
  envVars?: Record<string, string | number | boolean>;
}

interface TerminalMessage {
  type: 'start' | 'output' | 'error' | 'end';
  data: string;
  timestamp: number;
}

export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate = false, isShell = false, isBackup = false, isClone = false, containerId, storage, backupStorage, executionId: propExecutionId, cloneCount, hostnames, containerType, envVars }: TerminalProps) {
  const [isConnected, setIsConnected] = useState(false);
  const [isRunning, setIsRunning] = useState(false);
  const [isClient, setIsClient] = useState(false);
  const [mobileInput, setMobileInput] = useState('');
  const [showMobileInput, setShowMobileInput] = useState(false);
  const [lastInputSent, setLastInputSent] = useState<string | null>(null);
  const [isMobile, setIsMobile] = useState(false);
  const [isStopped, setIsStopped] = useState(false);
  const [isTerminalReady, setIsTerminalReady] = useState(false);
  const terminalRef = useRef<HTMLDivElement>(null);
  const xtermRef = useRef<any>(null);
  const fitAddonRef = useRef<any>(null);
  const wsRef = useRef<WebSocket | null>(null);
  const inputHandlerRef = useRef<((data: string) => void) | null>(null);
  const [executionId, setExecutionId] = useState(() => propExecutionId ?? `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`);
  
  // Update executionId when propExecutionId changes
  useEffect(() => {
    if (propExecutionId) {
      setExecutionId(propExecutionId);
    }
  }, [propExecutionId]);
  
  const effectiveExecutionId = propExecutionId ?? executionId;
  const isConnectingRef = useRef<boolean>(false);
  const hasConnectedRef = useRef<boolean>(false);

  const scriptName = scriptPath.split('/').pop() ?? scriptPath.split('\\').pop() ?? 'Unknown Script';

  const handleMessage = useCallback((message: TerminalMessage) => {
    if (!xtermRef.current) return;

    const timestamp = new Date(message.timestamp).toLocaleTimeString();
    const prefix = `[${timestamp}] `;
    
    switch (message.type) {
      case 'start':
        xtermRef.current.writeln(`${prefix}[START] ${message.data}`);
        setIsRunning(true);
        break;
      case 'output':
        // Write directly to terminal - xterm.js handles ANSI codes natively
        xtermRef.current.write(message.data);
        break;
      case 'error':
        // Check if this looks like ANSI terminal output (contains escape codes)
        if (message.data.includes('\x1B[') || message.data.includes('\u001b[')) {
          // This is likely terminal output sent to stderr, treat it as normal output
          xtermRef.current.write(message.data);
        } else if (message.data.includes('TERM environment variable not set')) {
          // This is a common warning, treat as normal output
          xtermRef.current.write(message.data);
        } else if (message.data.includes('exit code') && message.data.includes('clear')) {
          // This is a script error, show it with error prefix
          xtermRef.current.writeln(`${prefix}[ERROR] ${message.data}`);
        } else {
          // This is a real error, show it with error prefix
          xtermRef.current.writeln(`${prefix}[ERROR] ${message.data}`);
        }
        break;
      case 'end':
        setIsRunning(false);
        
        // Check if this is an LXC creation script
        const isLxcCreation = scriptPath.includes('ct/') || 
                             scriptPath.includes('create_lxc') || 
                             (containerId != null) ||
                             scriptName.includes('lxc') ||
                             scriptName.includes('container');
        
        if (isLxcCreation && message.data.includes('SSH script execution finished with code: 0')) {
          // Display prominent LXC creation completion message
          xtermRef.current.writeln('');
          xtermRef.current.writeln('#########################################');
          xtermRef.current.writeln('########## LXC CREATION FINISHED ########');
          xtermRef.current.writeln('#########################################');
          xtermRef.current.writeln('');
        } else {
          xtermRef.current.writeln(`${prefix}✅ ${message.data}`);
        }
        break;
    }
  }, [scriptPath, containerId, scriptName]);

  // Ensure we're on the client side
  useEffect(() => {
    setIsClient(true);
    // Detect mobile on mount
    setIsMobile(window.innerWidth < 768);
  }, []);

  useEffect(() => {
    // Only initialize on client side
    if (!isClient || !terminalRef.current || xtermRef.current) return;

    // Store ref value to avoid stale closure
    const terminalElement = terminalRef.current;

    // Use setTimeout to ensure DOM is fully ready
    const initTerminal = async () => {
      if (!terminalElement || xtermRef.current) return;

      // Dynamically import xterm modules to avoid SSR issues
      const { Terminal: XTerm } = await import('@xterm/xterm');
      const { FitAddon } = await import('@xterm/addon-fit');
      const { WebLinksAddon } = await import('@xterm/addon-web-links');

      // Use the mobile state
      
      const terminal = new XTerm({
        theme: {
          background: '#0d1117',
          foreground: '#e6edf3',
          cursor: '#58a6ff',
          cursorAccent: '#0d1117',
          // Let ANSI colors work naturally - only define basic colors
          black: '#484f58',
          red: '#f85149',
          green: '#3fb950',
          yellow: '#d29922',
          blue: '#58a6ff',
          magenta: '#bc8cff',
          cyan: '#39d353',
          white: '#b1bac4',
          brightBlack: '#6e7681',
          brightRed: '#ff7b72',
          brightGreen: '#56d364',
          brightYellow: '#e3b341',
          brightBlue: '#79c0ff',
          brightMagenta: '#d2a8ff',
          brightCyan: '#56d364',
          brightWhite: '#f0f6fc',
        },
        fontSize: isMobile ? 7 : 14,
        fontFamily: 'JetBrains Mono, Fira Code, Cascadia Code, Monaco, Menlo, Ubuntu Mono, monospace',
        cursorBlink: true,
        cursorStyle: 'block',
        scrollback: 1000,
        tabStopWidth: 4,
        allowTransparency: false,
        convertEol: true,
        disableStdin: false,
        macOptionIsMeta: false,
        rightClickSelectsWord: false,
        wordSeparator: ' ()[]{}\'"`<>|',
        // Better ANSI handling
        allowProposedApi: true,
        // Force proper terminal behavior for interactive applications
        // Use smaller dimensions on mobile but ensure proper fit
        cols: isMobile ? 45 : 80,
        rows: isMobile ? 18 : 24,
      });

      // Add addons
      const fitAddon = new FitAddon();
      const webLinksAddon = new WebLinksAddon();
      terminal.loadAddon(fitAddon);
      terminal.loadAddon(webLinksAddon);
      
      // Enable better ANSI handling
      terminal.options.allowProposedApi = true;

      // Open terminal
      terminal.open(terminalElement);
      
      // Ensure proper terminal rendering
      setTimeout(() => {
        terminal.refresh(0, terminal.rows - 1);
        // Ensure cursor is properly positioned
        terminal.focus();
        
        // Force focus on the terminal element
        terminalElement.focus();
        terminalElement.click();
        
        // Add click handler to ensure terminal stays focused
        const focusHandler = () => {
          terminal.focus();
          terminalElement.focus();
        };
        terminalElement.addEventListener('click', focusHandler);
        
        // Store the handler for cleanup
        (terminalElement as any).focusHandler = focusHandler;
      }, 100);
      
      // Fit after a small delay to ensure proper sizing
      setTimeout(() => {
        fitAddon.fit();
        // Force fit multiple times for mobile to ensure proper sizing
        if (isMobile) {
          setTimeout(() => {
            fitAddon.fit();
            setTimeout(() => {
              fitAddon.fit();
            }, 200);
          }, 300);
        }
      }, 100);

      // Add resize listener for mobile responsiveness
      const handleResize = () => {
        if (fitAddonRef.current) {
          setTimeout(() => {
            fitAddonRef.current.fit();
          }, 50);
        }
      };

      window.addEventListener('resize', handleResize);
      
      // Store the handler for cleanup
      (terminalElement as any).resizeHandler = handleResize;

      // Store references
      xtermRef.current = terminal;
      fitAddonRef.current = fitAddon;
      
      // Mark terminal as ready
      setIsTerminalReady(true);


      return () => {
        terminal.dispose();
      };
    };

    // Initialize with a small delay
    const timeoutId = setTimeout(() => {
      void initTerminal();
    }, 50);

      return () => {
        clearTimeout(timeoutId);
        if (terminalElement && (terminalElement as any).resizeHandler) {
          window.removeEventListener('resize', (terminalElement as any).resizeHandler as (this: Window, ev: UIEvent) => any);
        }
        if (terminalElement && (terminalElement as any).focusHandler) {
          terminalElement.removeEventListener('click', (terminalElement as any).focusHandler as (this: HTMLDivElement, ev: PointerEvent) => any);
        }
        if (xtermRef.current) {
          xtermRef.current.dispose();
          xtermRef.current = null;
          fitAddonRef.current = null;
          setIsTerminalReady(false);
        }
      };
  }, [isClient, isMobile]);

  // Handle terminal input with current executionId
  useEffect(() => {
    if (!isTerminalReady || !xtermRef.current) {
      return;
    }

    const terminal = xtermRef.current;
    
    const handleData = (data: string) => {
      if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
        const message = {
          action: 'input',
          executionId: effectiveExecutionId,
          input: data
        };
        wsRef.current.send(JSON.stringify(message));
      }
    };

    // Store the handler reference
    inputHandlerRef.current = handleData;
    terminal.onData(handleData);

    return () => {
      // Clear the handler reference
      inputHandlerRef.current = null;
    };
  }, [executionId, isTerminalReady]); // Depend on terminal ready state

  useEffect(() => {
    // Prevent multiple connections in React Strict Mode
    if (hasConnectedRef.current || isConnectingRef.current || (wsRef.current && wsRef.current.readyState === WebSocket.OPEN)) {
      return;
    }

    // Close any existing connection first
    if (wsRef.current) {
      wsRef.current.close();
      wsRef.current = null;
    }

    isConnectingRef.current = true;
    const isInitialConnection = !hasConnectedRef.current;
    hasConnectedRef.current = true;

    // Small delay to prevent rapid reconnection
    const connectWithDelay = () => {
      // Connect to WebSocket
      const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
      const wsUrl = `${protocol}//${window.location.host}/ws/script-execution`;
      
      const ws = new WebSocket(wsUrl);
      wsRef.current = ws;

      ws.onopen = () => {
        setIsConnected(true);
        isConnectingRef.current = false;
        
        // Only auto-start on initial connection, not on reconnections
        if (isInitialConnection && !isRunning) {
          // Use propExecutionId if provided, otherwise generate a new one
          const newExecutionId = propExecutionId ?? `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
          if (!propExecutionId) {
            setExecutionId(newExecutionId);
          }
          
          const message = {
            action: 'start',
            scriptPath,
            executionId: newExecutionId,
            mode,
            server,
            isUpdate,
            isShell,
            isBackup,
            isClone,
            containerId,
            storage,
            backupStorage,
            cloneCount,
            hostnames,
            containerType,
            envVars
          };
          ws.send(JSON.stringify(message));
        }
      };

      ws.onmessage = (event) => {
        try {
          const message = JSON.parse(event.data as string) as TerminalMessage;
          handleMessage(message);
        } catch (error) {
          console.error('Error parsing WebSocket message:', error);
        }
      };

      ws.onclose = (_event) => {
        setIsConnected(false);
        setIsRunning(false);
        isConnectingRef.current = false;
      };

      ws.onerror = (error) => {
        console.error('WebSocket error:', error);
        console.error('WebSocket readyState:', ws.readyState);
        setIsConnected(false);
        isConnectingRef.current = false;
      };
    };

    // Add small delay to prevent rapid reconnection
    const timeoutId = setTimeout(connectWithDelay, 100);

    return () => {
      clearTimeout(timeoutId);
      isConnectingRef.current = false;
      hasConnectedRef.current = false;
      if (wsRef.current && (wsRef.current.readyState === WebSocket.OPEN || wsRef.current.readyState === WebSocket.CONNECTING)) {
        wsRef.current.close();
      }
    };
  }, [scriptPath, mode, server, isUpdate, isShell, containerId, isMobile, envVars]);  

  const startScript = () => {
    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN && !isRunning) {
      // Generate a new execution ID for each script run (unless propExecutionId is provided)
      const newExecutionId = propExecutionId ?? `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
      if (!propExecutionId) {
        setExecutionId(newExecutionId);
      }
      
      setIsStopped(false);
      wsRef.current.send(JSON.stringify({
        action: 'start',
        scriptPath,
        executionId: newExecutionId,
        mode,
        server,
        envVars,
        isUpdate,
        isShell,
        isBackup,
        isClone,
        containerId,
        storage,
        backupStorage,
        cloneCount,
        hostnames,
        containerType
      }));
    }
  };

  const stopScript = () => {
    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
      setIsStopped(true);
      setIsRunning(false);
      wsRef.current.send(JSON.stringify({
        action: 'stop',
        executionId
      }));
    }
  };

  const clearOutput = () => {
    if (xtermRef.current) {
      xtermRef.current.clear();
    }
  };

  const sendInput = (input: string) => {
    setLastInputSent(input);
    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
      const message = {
        action: 'input',
        executionId,
        input: input
      };
      wsRef.current.send(JSON.stringify(message));
      // Clear the feedback after 2 seconds
      setTimeout(() => setLastInputSent(null), 2000);
    }
  };

  const handleMobileInput = (input: string) => {
    sendInput(input);
    setMobileInput('');
  };


  const handleEnterKey = () => {
    sendInput('\r');
  };

  // Don't render on server side
  if (!isClient) {
    return (
      <div className="bg-card rounded-lg border border-border overflow-hidden">
        <div className="bg-muted px-4 py-2 flex items-center justify-between border-b border-border">
          <div className="flex items-center space-x-2">
            <div className="flex space-x-1">
              <div className="w-3 h-3 bg-red-500 rounded-full"></div>
              <div className="w-3 h-3 bg-yellow-500 rounded-full"></div>
              <div className="w-3 h-3 bg-green-500 rounded-full"></div>
            </div>
            <span className="text-foreground font-mono text-sm ml-2">
              {scriptName}
            </span>
          </div>
        </div>
        <div className="h-96 w-full flex items-center justify-center">
          <div className="text-muted-foreground">Loading terminal...</div>
        </div>
      </div>
    );
  }

  return (
    <div className="bg-card rounded-lg border border-border overflow-hidden">
      {/* Terminal Header */}
      <div className="bg-muted px-2 sm:px-4 py-2 flex items-center justify-between border-b border-border">
        <div className="flex items-center space-x-2 min-w-0 flex-1">
          <div className="flex space-x-1 flex-shrink-0">
            <div className="w-2 h-2 sm:w-3 sm:h-3 bg-red-500 rounded-full"></div>
            <div className="w-2 h-2 sm:w-3 sm:h-3 bg-yellow-500 rounded-full"></div>
            <div className="w-2 h-2 sm:w-3 sm:h-3 bg-green-500 rounded-full"></div>
          </div>
          <span className="text-foreground font-mono text-xs sm:text-sm ml-1 sm:ml-2 truncate">
            {scriptName} {mode === 'ssh' && server && `(SSH: ${server.name})`}
          </span>
        </div>
        
        <div className="flex items-center space-x-1 sm:space-x-2 flex-shrink-0">
          <div className={`w-2 h-2 rounded-full ${isConnected ? 'bg-green-500' : 'bg-red-500'}`}></div>
          <span className="text-muted-foreground text-xs hidden sm:inline">
            {isConnected ? 'Connected' : 'Disconnected'}
          </span>
        </div>
      </div>

      {/* Terminal Output */}
      <div 
        ref={terminalRef}
        className={`h-[16rem] sm:h-[24rem] lg:h-[32rem] w-full max-w-4xl mx-auto ${isMobile ? 'mobile-terminal' : ''}`}
        style={{ 
          minHeight: '256px'
        }}
      />

      {/* Mobile Input Controls - Only show on mobile */}
      <div className="block sm:hidden bg-muted/50 px-2 py-3 border-t border-border">
        <div className="flex items-center justify-between mb-3">
          <div className="flex items-center gap-2">
            <span className="text-sm font-medium text-foreground">Mobile Input</span>
            {lastInputSent && (
              <span className="text-xs text-green-500 bg-green-500/10 px-2 py-1 rounded">
                Sent: {lastInputSent === '\r' ? 'Enter' : 
                       lastInputSent === ' ' ? 'Space' :
                       lastInputSent === '\b' ? 'Backspace' :
                       lastInputSent === '\x1b[A' ? 'Up' : 
                       lastInputSent === '\x1b[B' ? 'Down' : 
                       lastInputSent === '\x1b[C' ? 'Right' : 
                       lastInputSent === '\x1b[D' ? 'Left' : 
                       lastInputSent}
              </span>
            )}
          </div>
          <Button
            onClick={() => setShowMobileInput(!showMobileInput)}
            variant="ghost"
            size="sm"
            className="text-xs"
          >
            <Keyboard className="h-4 w-4 mr-1" />
            {showMobileInput ? 'Hide' : 'Show'} Input
          </Button>
        </div>
        
        {showMobileInput && (
          <div className="space-y-3">
            {/* Navigation Buttons */}
            <div className="grid grid-cols-2 gap-2">
              <Button
                onClick={() => sendInput('\x1b[A')}
                variant="outline"
                size="sm"
                className="text-sm flex items-center justify-center gap-2"
                disabled={!isConnected}
              >
                <ChevronUp className="h-4 w-4" />
                Up
              </Button>
              <Button
                onClick={() => sendInput('\x1b[B')}
                variant="outline"
                size="sm"
                className="text-sm flex items-center justify-center gap-2"
                disabled={!isConnected}
              >
                <ChevronDown className="h-4 w-4" />
                Down
              </Button>
            </div>
            
            {/* Left/Right Navigation Buttons */}
            <div className="grid grid-cols-2 gap-2">
              <Button
                onClick={() => sendInput('\x1b[D')}
                variant="outline"
                size="sm"
                className="text-sm flex items-center justify-center gap-2"
                disabled={!isConnected}
              >
                <ChevronLeft className="h-4 w-4" />
                Left
              </Button>
              <Button
                onClick={() => sendInput('\x1b[C')}
                variant="outline"
                size="sm"
                className="text-sm flex items-center justify-center gap-2"
                disabled={!isConnected}
              >
                <ChevronRight className="h-4 w-4" />
                Right
              </Button>
            </div>
            
            {/* Action Buttons */}
            <div className="grid grid-cols-3 gap-2">
              <Button
                onClick={handleEnterKey}
                variant="outline"
                size="sm"
                className="text-sm"
                disabled={!isConnected}
              >
                Enter
              </Button>
              <Button
                onClick={() => sendInput(' ')}
                variant="outline"
                size="sm"
                className="text-sm"
                disabled={!isConnected}
              >
                Space
              </Button>
              <Button
                onClick={() => sendInput('\b')}
                variant="outline"
                size="sm"
                className="text-sm"
                disabled={!isConnected}
              >
                ⌫ Backspace
              </Button>
            </div>
            
            {/* Custom Input */}
            <div className="flex gap-2">
              <input
                type="text"
                value={mobileInput}
                onChange={(e) => setMobileInput(e.target.value)}
                placeholder="Type command..."
                className="flex-1 px-3 py-2 text-sm border border-border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
                onKeyPress={(e) => {
                  if (e.key === 'Enter') {
                    handleMobileInput(mobileInput);
                  }
                }}
                disabled={!isConnected}
              />
              <Button
                onClick={() => handleMobileInput(mobileInput)}
                variant="default"
                size="sm"
                disabled={!isConnected || !mobileInput.trim()}
                className="px-3"
              >
                <Send className="h-4 w-4" />
              </Button>
            </div>
          </div>
        )}
      </div>

      {/* Terminal Controls */}
      <div className="bg-muted px-2 sm:px-4 py-2 flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-2 border-t border-border">
        <div className="flex flex-wrap gap-1 sm:gap-2">
          <Button
            onClick={startScript}
            disabled={!isConnected || (isRunning && !isStopped)}
            variant="default"
            size="sm"
            className={`text-xs sm:text-sm ${isConnected && (!isRunning || isStopped) ? 'bg-green-600 hover:bg-green-700' : 'bg-muted text-muted-foreground cursor-not-allowed'}`}
          >
            <Play className="h-3 w-3 sm:h-4 sm:w-4 mr-1" />
            <span className="hidden sm:inline">Start</span>
            <span className="sm:hidden">▶</span>
          </Button>
          
          <Button
            onClick={stopScript}
            disabled={!isRunning}
            variant="default"
            size="sm"
            className={`text-xs sm:text-sm ${isRunning ? 'bg-red-600 hover:bg-red-700' : 'bg-muted text-muted-foreground cursor-not-allowed'}`}
          >
            <Square className="h-3 w-3 sm:h-4 sm:w-4 mr-1" />
            <span className="hidden sm:inline">Stop</span>
            <span className="sm:hidden">⏹</span>
          </Button>
          
          <Button
            onClick={clearOutput}
            variant="secondary"
            size="sm"
            className="text-xs sm:text-sm bg-secondary text-secondary-foreground hover:bg-secondary/80"
          >
            <Trash2 className="h-3 w-3 sm:h-4 sm:w-4 mr-1" />
            <span className="hidden sm:inline">Clear</span>
            <span className="sm:hidden">🗑</span>
          </Button>
        </div>

        <Button
          onClick={onClose}
          variant="secondary"
          size="sm"
          className="text-xs sm:text-sm bg-gray-600 text-white hover:bg-gray-700 w-full sm:w-auto"
        >
          <X className="h-3 w-3 sm:h-4 sm:w-4 mr-1" />
          Close
        </Button>
      </div>
    </div>
  );
}