import type { CSSProperties } from 'react';
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import 'xterm/css/xterm.css';

export interface XTermHandle {
  write: (text: string) => void;
  clear: () => void;
}

export interface XTermProps {
  onData?: (data: string) => void;
  cursorBlink?: boolean;
  className?: string;
  style?: CSSProperties;
  disabled?: boolean;
  welcomeMessage?: string;
  theme?: {
    background?: string;
    foreground?: string;
    cursor?: string;
    selectionBackground?: string;
  };
}

const DEFAULT_WELCOME = 'Welcome to Smatec IoT Agent';

const XTerm = forwardRef<XTermHandle, XTermProps>(function XTerm(
  {
    onData,
    cursorBlink = false,
    className,
    style,
    disabled = false,
    welcomeMessage = DEFAULT_WELCOME,
    theme,
  },
  ref,
) {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const terminalRef = useRef<Terminal | null>(null);
  const fitAddonRef = useRef<FitAddon | null>(null);
  const onDataRef = useRef(onData);
  const disabledRef = useRef(disabled);

  useImperativeHandle(
    ref,
    () => ({
      write: (text: string) => {
        if (terminalRef.current && text) {
          terminalRef.current.write(text);
        }
      },
      clear: () => {
        terminalRef.current?.clear();
      },
    }),
    [],
  );

  useEffect(() => {
    onDataRef.current = onData;
  }, [onData]);

  useEffect(() => {
    disabledRef.current = disabled;
    // Update terminal's disableStdin option when disabled prop changes
    if (terminalRef.current) {
      terminalRef.current.options.disableStdin = disabled;
    }
  }, [disabled]);

  useEffect(() => {
    const terminal = new Terminal({
      cursorBlink,
      scrollback: 1000,
      convertEol: true,
      fontSize: 14,
      disableStdin: disabled,
      allowProposedApi: true,
      theme,
    });
    const fitAddon = new FitAddon();
    terminal.loadAddon(fitAddon);

    if (containerRef.current) {
      terminal.open(containerRef.current);
      fitAddon.fit();
    }

    if (welcomeMessage) {
      terminal.writeln(welcomeMessage);
    }

    const dataDisposable = terminal.onData((data) => {
      if (!disabledRef.current) {
        onDataRef.current?.(data);
      }
    });

    terminalRef.current = terminal;
    fitAddonRef.current = fitAddon;

    const observer =
      typeof ResizeObserver !== 'undefined' && containerRef.current
        ? new ResizeObserver(() => {
            fitAddon.fit();
          })
        : null;
    if (observer && containerRef.current) {
      observer.observe(containerRef.current);
    }

    return () => {
      dataDisposable.dispose();
      observer?.disconnect();
      terminal.dispose();
      fitAddon.dispose();
      terminalRef.current = null;
      fitAddonRef.current = null;
    };
    // We intentionally want to run this once on mount.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    if (terminalRef.current && theme) {
      terminalRef.current.options.theme = theme;
    }
  }, [theme]);

  return (
    <div
      className={className}
      style={{ height: '100%', ...style }}
      ref={containerRef}
    />
  );
});

export default XTerm;
