import type { PasteEvent } from '@opentui/core';
import { useKeyboard, useRenderer, useTerminalDimensions } from '@opentui/react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { updateFeed } from '../../fetchers/feed';
import { sanitizeTerminalText } from '../../utils/terminal';
import { validateUrl } from '../../utils/validation';
import { getArticleLoadState, isSameArticleLoadState, refreshVisibleData } from '../hooks/useDataLoader';
import { computeOverlayRect } from '../layout';
import { isTuiShutdownError, useTuiLifecycle } from '../lifecycle';
import { useAppDispatch, useAppState } from '../state/context';
import { limitGraphemes, removeLastGrapheme, terminalCellWidth, truncateTerminalText } from '../text';
import { colors } from '../theme';

type Field = 'title' | 'url' | 'category';
const fieldCycle: Field[] = ['title', 'url', 'category'];
const MAX_TITLE_GRAPHEMES = 512;
const MAX_URL_GRAPHEMES = 4_096;
const MAX_CATEGORY_GRAPHEMES = 256;

export function EditFeedOverlay() {
  const state = useAppState();
  const dispatch = useAppDispatch();
  const lifecycle = useTuiLifecycle();
  const renderer = useRenderer();
  const { width: terminalWidth, height: terminalHeight } = useTerminalDimensions();
  const feed = state.editingFeed;

  const [title, setTitle] = useState(() =>
    limitGraphemes(sanitizeTerminalText(feed?.title ?? ''), MAX_TITLE_GRAPHEMES),
  );
  const [url, setUrl] = useState(() => limitGraphemes(sanitizeTerminalText(feed?.xmlUrl ?? ''), MAX_URL_GRAPHEMES));
  const [category, setCategory] = useState(() =>
    limitGraphemes(sanitizeTerminalText(feed?.category ?? ''), MAX_CATEGORY_GRAPHEMES),
  );
  const [activeField, setActiveField] = useState<Field>('title');
  const [error, setError] = useState<string | null>(null);
  const [saving, setSaving] = useState(false);
  const mountedRef = useRef(true);
  const operationRef = useRef(0);
  const stateRef = useRef(state);
  stateRef.current = state;
  const layout = computeOverlayRect(terminalWidth, terminalHeight, {
    widthRatio: 0.6,
    minWidth: 28,
    height: 14,
    leftRatio: 0.2,
    topRatio: 0.3,
  });
  const labelWidth = Math.min(12, layout.contentWidth);
  const rowGap = layout.contentHeight >= 7 ? 1 : 0;
  const valueWidth = Math.max(0, layout.contentWidth - labelWidth - 1);

  const appendText = useCallback(
    (rawText: string) => {
      const text = sanitizeTerminalText(rawText);
      if (!text) return;
      if (activeField === 'title') setTitle((value) => limitGraphemes(value + text, MAX_TITLE_GRAPHEMES));
      else if (activeField === 'url') setUrl((value) => limitGraphemes(value + text, MAX_URL_GRAPHEMES));
      else setCategory((value) => limitGraphemes(value + text, MAX_CATEGORY_GRAPHEMES));
    },
    [activeField],
  );

  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]);

  useEffect(() => {
    mountedRef.current = true;
    return () => {
      mountedRef.current = false;
      operationRef.current += 1;
    };
  }, []);

  useKeyboard((key) => {
    if (key.name === 'escape') {
      // Save is not cancellable safely once it reaches the database.
      if (saving) return;
      operationRef.current += 1;
      dispatch({ type: 'SET_EDITING_FEED', feed: null });
      return;
    }

    if (key.name === 'tab') {
      setActiveField((f) => fieldCycle[(fieldCycle.indexOf(f) + 1) % fieldCycle.length] as Field);
      return;
    }

    if ((key.name === 'enter' || key.name === 'return') && !saving) {
      if (!title.trim()) {
        setError('Title is required');
        return;
      }
      if (!url.trim()) {
        setError('URL is required');
        return;
      }
      let normalizedUrl = url.trim();
      if (!/^https?:\/\//i.test(normalizedUrl)) {
        normalizedUrl = `https://${normalizedUrl}`;
      }
      const urlValidation = validateUrl(normalizedUrl);
      if (!urlValidation.valid) {
        setError(urlValidation.error);
        return;
      }
      if (!feed) return;
      setSaving(true);
      setError(null);
      const updates: Parameters<typeof updateFeed>[1] = {
        title: title.trim(),
        category: category.trim() || undefined,
      };
      const originalUrl = /^https?:\/\//i.test(feed.xmlUrl) ? feed.xmlUrl : `https://${feed.xmlUrl}`;
      if (normalizedUrl !== originalUrl) {
        updates.xmlUrl = normalizedUrl;
      }
      const operation = operationRef.current + 1;
      operationRef.current = operation;
      const feedId = feed.id;
      const criteria = getArticleLoadState(stateRef.current);
      const isActive = () =>
        mountedRef.current &&
        operationRef.current === operation &&
        stateRef.current.overlay === 'editFeed' &&
        stateRef.current.editingFeed?.id === feedId &&
        lifecycle.isAcceptingWork() &&
        isSameArticleLoadState(criteria, getArticleLoadState(stateRef.current));

      void lifecycle
        .run(async () => {
          await updateFeed(feedId, updates);
          if (!isActive()) return;
          await refreshVisibleData(criteria, dispatch, { canDispatch: isActive });
          if (isActive()) dispatch({ type: 'SET_EDITING_FEED', feed: null });
        })
        .catch((err) => {
          if (!isTuiShutdownError(err) && isActive()) {
            setError(sanitizeTerminalText(err instanceof Error ? err.message : String(err)));
            setSaving(false);
          }
        });
      return;
    }

    if (key.name === 'backspace') {
      if (activeField === 'title') setTitle((value) => removeLastGrapheme(value));
      else if (activeField === 'url') setUrl((value) => removeLastGrapheme(value));
      else setCategory((value) => removeLastGrapheme(value));
      return;
    }

    // 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;
    }
  });

  if (!feed) return null;

  return (
    <box
      position="absolute"
      left={layout.left}
      top={layout.top}
      width={layout.width}
      height={layout.height}
      border
      borderStyle="rounded"
      borderColor={colors.accentBlue}
      title={truncateTerminalText(' Edit Feed ', Math.max(0, layout.width - 4))}
      titleAlignment="center"
      backgroundColor={colors.bg}
      padding={layout.padding}
      zIndex={100}
      flexDirection="column"
    >
      {layout.contentHeight >= 1 ? (
        <box flexDirection="row" marginBottom={rowGap}>
          <box width={labelWidth}>
            <text fg={colors.textSecondary}>{truncateTerminalText('Title:', labelWidth)}</text>
          </box>
          <text>
            <span fg={activeField === 'title' ? colors.textPrimary : colors.textSecondary}>
              {truncateTerminalText(sanitizeTerminalText(title) || '(enter title)', valueWidth)}
            </span>
            {activeField === 'title' && terminalCellWidth(title || '(enter title)') < valueWidth ? (
              <span fg={colors.accentBlue}>{'█'}</span>
            ) : null}
          </text>
        </box>
      ) : null}

      {layout.contentHeight >= 2 + rowGap ? (
        <box flexDirection="row" marginBottom={rowGap}>
          <box width={labelWidth}>
            <text fg={colors.textSecondary}>{truncateTerminalText('URL:', labelWidth)}</text>
          </box>
          <text>
            <span fg={activeField === 'url' ? colors.textPrimary : colors.textSecondary}>
              {truncateTerminalText(sanitizeTerminalText(url) || '(enter url)', valueWidth)}
            </span>
            {activeField === 'url' && terminalCellWidth(url || '(enter url)') < valueWidth ? (
              <span fg={colors.accentBlue}>{'█'}</span>
            ) : null}
          </text>
        </box>
      ) : null}

      {layout.contentHeight >= 3 + rowGap * 2 ? (
        <box flexDirection="row" marginBottom={rowGap}>
          <box width={labelWidth}>
            <text fg={colors.textSecondary}>{truncateTerminalText('Category:', labelWidth)}</text>
          </box>
          <text>
            <span fg={activeField === 'category' ? colors.textPrimary : colors.textSecondary}>
              {truncateTerminalText(sanitizeTerminalText(category) || '(optional)', valueWidth)}
            </span>
            {activeField === 'category' && terminalCellWidth(category || '(optional)') < valueWidth ? (
              <span fg={colors.accentBlue}>{'█'}</span>
            ) : null}
          </text>
        </box>
      ) : null}

      {layout.contentHeight >= 4 + rowGap * 3 ? (
        error ? (
          <text fg={colors.accentRed}>{truncateTerminalText(sanitizeTerminalText(error), layout.contentWidth)}</text>
        ) : saving ? (
          <text fg={colors.accentGreen}>{truncateTerminalText('Saving... Esc is disabled', layout.contentWidth)}</text>
        ) : (
          <text fg={colors.textMuted}>
            {truncateTerminalText('Tab to switch fields, Enter to save, Esc to cancel', layout.contentWidth)}
          </text>
        )
      ) : null}
    </box>
  );
}
