import { type Result } from '@scrabble-solver/types';
import classNames from 'classnames';
import { type FunctionComponent, memo, type SyntheticEvent, useEffect, useMemo } from 'react';
import { useDispatch } from 'react-redux';

import { useIsCompactLayout } from '@/hooks/useIsCompactLayout';
import { useIsTouchDevice } from '@/hooks/useIsTouchDevice';
import {
  resultsSlice,
  selectAreResultsOutdated,
  selectProcessedResults,
  selectResultCandidate,
  selectResultCandidateIndex,
  selectSolveError,
  solveSlice,
  useTranslate,
  useTypedSelector,
  useTypedStore,
} from '@/state';

import { Alert } from '../Alert';
import { Board } from '../Board';
import { Dictionary } from '../Dictionary';
import { DictionaryInput } from '../DictionaryInput';
import { Rack } from '../Rack';
import { Results } from '../Results';

import { ResultCandidatePicker } from './components';
import styles from './Solver.module.scss';

interface Props {
  className?: string;
  onShowResults: () => void;
}

const SolverBase: FunctionComponent<Props> = ({ className, onShowResults }) => {
  const dispatch = useDispatch();
  const translate = useTranslate();
  const isTouchDevice = useIsTouchDevice();
  const showCompactControls = useIsCompactLayout();
  const error = useTypedSelector(selectSolveError);
  const isOutdated = useTypedSelector(selectAreResultsOutdated);
  const results = useTypedSelector(selectProcessedResults);
  const highlightedIndex = useTypedSelector(selectResultCandidateIndex);
  const store = useTypedStore();
  const [bestResult] = results || [];
  const touchCallbacks = useMemo(
    () => ({
      onClick: (result: Result) => {
        const isSelected = result === selectResultCandidate(store.getState());

        if (isSelected) {
          dispatch(resultsSlice.actions.applyResult(result));
        } else {
          dispatch(resultsSlice.actions.changeResultCandidate(result));
        }
      },
    }),
    [dispatch, store],
  );
  const mouseCallbacks = useMemo(
    () => ({
      onBlur: () => {
        dispatch(resultsSlice.actions.changeResultCandidate(null));
      },
      onClick: (result: Result) => {
        dispatch(resultsSlice.actions.applyResult(result));
      },
      onFocus: (result: Result) => {
        dispatch(resultsSlice.actions.changeResultCandidate(result));
      },
      onMouseEnter: (result: Result) => {
        dispatch(resultsSlice.actions.changeResultCandidate(result));
      },
      onMouseLeave: () => {
        dispatch(resultsSlice.actions.changeResultCandidate(null));
      },
    }),
    [dispatch],
  );
  const callbacks = isTouchDevice ? touchCallbacks : mouseCallbacks;

  const handleSubmit = (event: SyntheticEvent) => {
    event.preventDefault();

    dispatch(solveSlice.actions.submit());
  };

  useEffect(() => {
    if (showCompactControls && bestResult && !isOutdated) {
      dispatch(resultsSlice.actions.changeResultCandidate(bestResult));
    }
  }, [bestResult, dispatch, showCompactControls, isOutdated]);

  return (
    <div className={classNames(styles.solver, className)}>
      <div className={styles.container}>
        <div className={styles.content}>
          <form className={styles.boardContainer} onSubmit={handleSubmit}>
            <Board className={styles.board} />
            <input className={styles.submitInput} tabIndex={-1} type="submit" />
          </form>

          <div className={styles.column}>
            <Results callbacks={callbacks} className={styles.results} highlightedIndex={highlightedIndex} />

            <div data-testid="dictionary" className={styles.dictionaryContainer}>
              <Dictionary className={styles.dictionary} />
              <DictionaryInput className={styles.dictionaryInput} />
            </div>
          </div>
        </div>
      </div>

      <div className={styles.bottomContainer}>
        <div className={styles.bottomContent}>
          <form onSubmit={handleSubmit}>
            <Rack />
            <input className={styles.submitInput} tabIndex={-1} type="submit" />
          </form>

          <div className={styles.controls}>
            <ResultCandidatePicker onResultClick={onShowResults} />

            {error && (
              <Alert className={styles.emptyState} variant="error">
                {error.message}
              </Alert>
            )}

            {results && results.length === 0 && !isOutdated && (
              <Alert className={styles.emptyState} variant="warning">
                {translate('results.empty-state.no-results')}
              </Alert>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

export const Solver = memo(SolverBase);
