import React, {
  createContext,
  Dispatch,
  FunctionComponent,
  Reducer,
  useMemo,
  useReducer,
} from 'react';

import twl06 = require('./twl06.json');

export interface State {
  currentWord: string;
  letters: string[];
  score: number;
  view: 'GAME' | 'SETUP';
  words: string[];
}

const initialState: State = {
  currentWord: '',
  letters: [],
  score: 0,
  view: 'SETUP',
  words: [],
};

export interface DeleteLetterAction {
  type: 'DELETE_LETTER';
}

export interface EnterLetterAction {
  type: 'ENTER_LETTER';
  payload: string;
}

export interface EnterWordAction {
  type: 'ENTER_WORD';
}

export interface SetLettersAction {
  type: 'SET_LETTERS';
  payload: string[];
}

export interface ShuffleLettersAction {
  type: 'SHUFFLE_LETTERS';
}

export interface SwitchViewAction {
  type: 'SWITCH_VIEW';
  payload: State['view'];
}

type Action = DeleteLetterAction
            | EnterLetterAction
            | EnterWordAction
            | SetLettersAction
            | ShuffleLettersAction
            | SwitchViewAction;

const shuffle = (letters: string[]): string[] => {
  const newLetters = letters.slice(0, 1);
  const choices = letters.slice(1);
  for (let i = 1; i < letters.length; i += 1) {
    const choice = Math.floor(Math.random() * choices.length);
    newLetters.push(choices.splice(choice, 1)[0]);
  }
  return newLetters;
};

const canSubmitWord = (state: State): boolean => {
  const { currentWord, words } = state;
  return currentWord.length > 3 && !words.includes(currentWord)
    && currentWord.split('').includes(state.letters[0])
    && twl06.includes(currentWord.toLowerCase());
};

const scoreWord = (word: string): number => (word.length === 4 ? 1 : word.length);

const enterWordReducer: Reducer<State, EnterWordAction> = (state) => {
  const valid = canSubmitWord(state);
  return {
    ...state,
    currentWord: '',
    score: state.score + (valid ? scoreWord(state.currentWord) : 0),
    words: valid ? [...state.words, state.currentWord] : state.words,
  };
};

const reducer: Reducer<State, Action> = (state, action) => {
  switch (action.type) {
    case 'DELETE_LETTER':
      return { ...state, currentWord: state.currentWord.slice(0, -1) };
    case 'ENTER_LETTER':
      return { ...state, currentWord: `${state.currentWord}${action.payload}` };
    case 'ENTER_WORD':
      return enterWordReducer(state, action);
    case 'SET_LETTERS':
      return action.payload.length === state.letters.length
        && action.payload.every((ch, i) => state.letters[i] === ch)
        ? state
        : {
            ...state,
            currentWord: '',
            letters: action.payload,
            score: 0,
            words: [],
          };
    case 'SHUFFLE_LETTERS':
      return { ...state, letters: shuffle(state.letters) };
    case 'SWITCH_VIEW':
      return { ...state, view: action.payload };
    default:
      throw new Error();
  }
};

interface ContextValue {
  state: State;
  dispatch: Dispatch<Action>;
}

export const StoreContext = createContext({
  state: initialState,
  dispatch: () => undefined,
} as ContextValue);

export const StoreProvider: FunctionComponent = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const ctxVal = useMemo(() => ({ state, dispatch }), [state, dispatch]);
  return (
    <StoreContext.Provider value={ctxVal}>
      {children}
    </StoreContext.Provider>
  );
};
