import React, { useEffect, useRef, useState, useCallback } from "react";
import { Terminal } from "xterm";
import { FitAddon } from "xterm-addon-fit";
import { WebLinksAddon } from "xterm-addon-web-links";
import "xterm/css/xterm.css";

interface TerminalProps {
  instanceId: string;
  onClose?: () => void;
  isMobile?: boolean;
  onTerminalReady?: (
    sendData: (data: string) => void,
    connectionStatus: "connecting" | "connected" | "disconnected" | "error",
    focusInput: () => void
  ) => void;
}

const FONT_SIZE_KEY = "terminal-font-size";
const MIN_FONT_SIZE = 16;
const MAX_FONT_SIZE = 24;
const RECONNECT_ATTEMPTS = 3;
const RECONNECT_DELAY_BASE = 1000;

export const TerminalComponent: React.FC<TerminalProps> = ({
  instanceId,
  onClose,
  isMobile = false,
  onTerminalReady,
}) => {
  const terminalRef = useRef<HTMLDivElement>(null);
  const hiddenInputRef = useRef<HTMLInputElement>(null);
  const wsRef = useRef<WebSocket | null>(null);
  const termRef = useRef<Terminal | null>(null);
  const fitAddonRef = useRef<FitAddon | null>(null);
  const reconnectAttemptsRef = useRef(0);
  const onTerminalReadyRef = useRef(onTerminalReady);
  onTerminalReadyRef.current = onTerminalReady;
  const [status, setStatus] = useState<
    "connecting" | "connected" | "disconnected" | "error"
  >("connecting");
  const [error, setError] = useState<string | null>(null);
  const [fontSize, setFontSize] = useState(() => {
    if (typeof window === "undefined") return isMobile ? 16 : 14;
    const stored = localStorage.getItem(FONT_SIZE_KEY);
    return stored ? parseInt(stored, 10) : isMobile ? 16 : 14;
  });

  const calculateFontSize = useCallback(() => {
    if (!isMobile) return fontSize;
    const vw = window.innerWidth;
    const calculated = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, vw / 25));
    return Math.round(calculated);
  }, [isMobile, fontSize]);

  const connectWebSocket = useCallback(() => {
    const apiUrl = import.meta.env.VITE_API_BASE_URL || "";
    const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
    const wsHost = apiUrl.replace(/^https?:\/\//, "").replace(/\/+$/, "");
    const wsUrl = `${wsProtocol}//${wsHost}/ws/tool-instances/${instanceId}/terminal`;

    const ws = new WebSocket(wsUrl);
    wsRef.current = ws;

    ws.onopen = () => {
      setStatus("connected");
      setError(null);
      reconnectAttemptsRef.current = 0;
    };

    ws.onmessage = (event) => {
      if (!termRef.current) return;

      if (event.data instanceof Blob) {
        event.data.arrayBuffer().then((buffer) => {
          const data = new Uint8Array(buffer);
          termRef.current?.write(data);
        });
      } else if (typeof event.data === "string") {
        try {
          const msg = JSON.parse(event.data);
          if (msg.type === "status" && msg.status === "connected") {
            setStatus("connected");
          }
        } catch {
          termRef.current?.write(event.data);
        }
      }
    };

    ws.onclose = (event) => {
      setStatus("disconnected");
      if (event.code !== 1000) {
        setError(`Connection closed (code: ${event.code})`);

        // Attempt reconnection
        if (reconnectAttemptsRef.current < RECONNECT_ATTEMPTS) {
          reconnectAttemptsRef.current++;
          const delay = RECONNECT_DELAY_BASE * Math.pow(2, reconnectAttemptsRef.current - 1);
          setTimeout(() => {
            if (document.visibilityState !== "hidden") {
              connectWebSocket();
            }
          }, delay);
        }
      }
    };

    ws.onerror = () => {
      setStatus("error");
      setError("WebSocket error");
    };

    return ws;
  }, [instanceId]);

  useEffect(() => {
    if (!terminalRef.current) return;

    // Initialize terminal
    const currentFontSize = calculateFontSize();
    const term = new Terminal({
      cursorBlink: true,
      fontSize: currentFontSize,
      fontFamily: 'Menlo, Monaco, "Courier New", monospace',
      theme: {
        background: "#1e1e1e",
        foreground: "#d4d4d4",
        cursor: "#d4d4d4",
        selectionBackground: "#264f78",
        black: "#000000",
        red: "#cd3131",
        green: "#0dbc79",
        yellow: "#e5e510",
        blue: "#2472c8",
        magenta: "#bc3fbc",
        cyan: "#11a8cd",
        white: "#e5e5e5",
        brightBlack: "#666666",
        brightRed: "#f14c4c",
        brightGreen: "#23d18b",
        brightYellow: "#f5f543",
        brightBlue: "#3b8eea",
        brightMagenta: "#d670d6",
        brightCyan: "#29b8db",
        brightWhite: "#e5e5e5",
      },
    });

    termRef.current = term;

    const fitAddon = new FitAddon();
    fitAddonRef.current = fitAddon;
    term.loadAddon(fitAddon);
    term.loadAddon(new WebLinksAddon());

    term.open(terminalRef.current);
    fitAddon.fit();

    // Connect WebSocket
    const ws = connectWebSocket();

    // Handle terminal input
    term.onData((data) => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(data);
      }
    });

    // Handle resize with debounce
    let resizeTimeout: ReturnType<typeof setTimeout>;
    const handleResize = () => {
      clearTimeout(resizeTimeout);
      resizeTimeout = setTimeout(() => {
        fitAddon.fit();
        const { cols, rows } = term;
        if (ws.readyState === WebSocket.OPEN) {
          ws.send(
            JSON.stringify({
              type: "resize",
              cols,
              rows,
            })
          );
        }
      }, 250);
    };

    window.addEventListener("resize", handleResize);

    // Initial resize
    setTimeout(handleResize, 100);

    // Notify parent about terminal readiness
    if (onTerminalReadyRef.current) {
      const sendData = (data: string) => {
        if (ws.readyState === WebSocket.OPEN) {
          ws.send(data);
        }
      };
      const focusInput = () => {
        termRef.current?.focus();
      };
      onTerminalReadyRef.current(sendData, status, focusInput);
    }

    // Visibility API for reconnection
    const handleVisibilityChange = () => {
      if (document.visibilityState === "visible" && ws.readyState !== WebSocket.OPEN) {
        reconnectAttemptsRef.current = 0;
        connectWebSocket();
      }
    };
    document.addEventListener("visibilitychange", handleVisibilityChange);

    return () => {
      clearTimeout(resizeTimeout);
      window.removeEventListener("resize", handleResize);
      document.removeEventListener("visibilitychange", handleVisibilityChange);
      ws.close();
      term.dispose();
    };
  }, [instanceId, connectWebSocket, calculateFontSize]);

  // Update parent about status changes
  useEffect(() => {
    if (onTerminalReady && termRef.current) {
      const sendData = (data: string) => {
        if (wsRef.current?.readyState === WebSocket.OPEN) {
          wsRef.current.send(data);
        }
      };
      const focusInput = () => {
        termRef.current?.focus();
      };
      onTerminalReady(sendData, status, focusInput);
    }
  }, [status, onTerminalReady]);

  const handleFontSizeChange = (delta: number) => {
    const newSize = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, fontSize + delta));
    setFontSize(newSize);
    localStorage.setItem(FONT_SIZE_KEY, newSize.toString());
    if (termRef.current) {
      termRef.current.options.fontSize = newSize;
      fitAddonRef.current?.fit();
    }
  };

  const handleCopy = async () => {
    if (!termRef.current) return;
    const selection = termRef.current.getSelection();
    if (selection) {
      try {
        await navigator.clipboard.writeText(selection);
      } catch {
        // Fallback for older browsers
        const textarea = document.createElement("textarea");
        textarea.value = selection;
        document.body.appendChild(textarea);
        textarea.select();
        document.execCommand("copy");
        document.body.removeChild(textarea);
      }
    }
  };

  const handlePaste = async () => {
    try {
      const text = await navigator.clipboard.readText();
      if (wsRef.current?.readyState === WebSocket.OPEN) {
        wsRef.current.send(text);
      }
    } catch {
      // Clipboard API not available
    }
  };

  // Focus terminal on mobile to keep keyboard open
  const handleTerminalClick = () => {
    if (isMobile && termRef.current) {
      termRef.current.focus();
    }
  };

  return (
    <div className={`terminal-wrapper ${isMobile ? "mobile" : ""}`}>
      <div className="terminal-header">
        <div className="terminal-header-left">
          <div className="terminal-status">
            <span
              className={`status-dot ${status}`}
              aria-label={`Terminal status: ${status}`}
            />
            <span className="status-text">
              {reconnectAttemptsRef.current > 0 && status !== "connected"
                ? `Reconnecting (${reconnectAttemptsRef.current}/${RECONNECT_ATTEMPTS})...`
                : status}
            </span>
          </div>
          {isMobile && (
            <>
              <button
                className="terminal-header-button"
                onClick={handleCopy}
                type="button"
                aria-label="Copy selection"
              >
                Copy
              </button>
              <button
                className="terminal-header-button"
                onClick={handlePaste}
                type="button"
                aria-label="Paste from clipboard"
              >
                Paste
              </button>
            </>
          )}
        </div>
        <div className="terminal-header-right">
          {isMobile && (
            <>
              <button
                className="terminal-header-button"
                onClick={() => handleFontSizeChange(-1)}
                type="button"
                aria-label="Decrease font size"
              >
                A-
              </button>
              <button
                className="terminal-header-button"
                onClick={() => handleFontSizeChange(1)}
                type="button"
                aria-label="Increase font size"
              >
                A+
              </button>
            </>
          )}
          {onClose && (
            <button className="terminal-close" onClick={onClose} type="button">
              Close
            </button>
          )}
        </div>
      </div>
      {error && (
        <div className="terminal-error">
          {error}
          {status === "error" && (
            <button
              className="terminal-reconnect"
              onClick={() => {
                reconnectAttemptsRef.current = 0;
                connectWebSocket();
              }}
              type="button"
            >
              Reconnect
            </button>
          )}
        </div>
      )}
      <div
        ref={terminalRef}
        className="terminal-container"
        onClick={handleTerminalClick}
      />
      {isMobile && (
        <input
          ref={hiddenInputRef}
          type="text"
          className="terminal-hidden-input"
          aria-hidden="true"
        />
      )}
    </div>
  );
};
