import type { PasteEvent } from '@opentui/core';
import { useKeyboard, useRenderer, useTerminalDimensions } from '@opentui/react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { sanitizeTerminalText } from '../../utils/terminal';
import { useAppDispatch, useAppState } from '../state/context';
import type { Action } from '../state/types';
import { limitGraphemes, removeLastGrapheme, terminalCellWidth, truncateTerminalText } from '../text';
import { colors } from '../theme';

export const MAX_SEARCH_GRAPHEMES = 512;

export function commitSearch(query: string, dispatch: React.Dispatch<Action>): void {
  dispatch({ type: 'SET_SEARCH_QUERY', query });
  dispatch({ type: 'SET_OVERLAY', overlay: 'none' });
  dispatch({ type: 'SET_PANEL', panel: 'articles' });
}

export function SearchBar() {
  const state = useAppState();
  const dispatch = useAppDispatch();
  const renderer = useRenderer();
  const { width: terminalWidth } = useTerminalDimensions();
  const initialQuery = limitGraphemes(sanitizeTerminalText(state.searchQuery), MAX_SEARCH_GRAPHEMES);
  const [query, setQuery] = useState(initialQuery);
  const initialQueryRef = useRef(initialQuery);
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const appendText = useCallback((rawText: string) => {
    const text = sanitizeTerminalText(rawText);
    if (text) setQuery((value) => limitGraphemes(value + text, MAX_SEARCH_GRAPHEMES));
  }, []);
  const prefix = '/  ';
  const instruction = '  (Enter to confirm, Esc to cancel)';
  const contentWidth = Math.max(0, terminalWidth - 1);
  const fixedWidth = terminalCellWidth(prefix) + 1;
  const showInstruction = contentWidth - fixedWidth >= terminalCellWidth(instruction) + 4;
  const queryWidth = Math.max(0, contentWidth - fixedWidth - (showInstruction ? terminalCellWidth(instruction) : 0));

  useEffect(() => {
    const handlePaste = (event: PasteEvent) => {
      appendText(new TextDecoder().decode(event.bytes));
      event.preventDefault();
    };
    renderer.keyInput.on('paste', handlePaste);
    return () => {
      renderer.keyInput.off('paste', handlePaste);
    };
  }, [renderer, appendText]);

  // Debounced search
  useEffect(() => {
    if (debounceRef.current) {
      clearTimeout(debounceRef.current);
    }
    debounceRef.current = setTimeout(() => {
      dispatch({ type: 'SET_SEARCH_QUERY', query });
    }, 150);
    return () => {
      if (debounceRef.current) clearTimeout(debounceRef.current);
    };
  }, [query, dispatch]);

  useKeyboard((key) => {
    if (key.name === 'escape') {
      // Cancel search, restore previous query
      dispatch({ type: 'SET_SEARCH_QUERY', query: initialQueryRef.current });
      dispatch({ type: 'SET_OVERLAY', overlay: 'none' });
      return;
    }

    if (key.name === 'enter' || key.name === 'return') {
      // Confirm search
      commitSearch(query, dispatch);
      return;
    }

    if (key.name === 'backspace') {
      setQuery((value) => removeLastGrapheme(value));
      return;
    }

    // Regular character input. Use key.sequence (not key.name) so shifted
    // letters keep their case — opentui lowercases key.name for them.
    if (!key.ctrl && !key.meta && key.name !== 'space') {
      appendText(key.sequence);
      return;
    }

    if (key.name === 'space') {
      appendText(' ');
      return;
    }
  });

  return (
    <box
      position="absolute"
      bottom={0}
      left={0}
      width="100%"
      height={1}
      backgroundColor={colors.bgHighlight}
      paddingLeft={1}
      zIndex={100}
    >
      <text>
        <span fg={colors.accentBlue}>{prefix}</span>
        <span fg={colors.textPrimary}>{truncateTerminalText(query, queryWidth)}</span>
        {queryWidth > 0 ? <span fg={colors.accentBlue}>{'█'}</span> : null}
        {showInstruction ? <span fg={colors.textMuted}>{instruction}</span> : null}
      </text>
    </box>
  );
}
