import React, { useCallback, useMemo } from 'react';
import { StoredCommand, CommandCategory } from '../types';
import useStorageBackedState from './useStorageBackedState';

export type SaveableCommand = StoredCommand & {
  lastSavedState?: StoredCommand
};

const generateDefaultCommand = (index: number) => {
  const command = {
    id: `untitled-${index}`,
    name: `Untitled-${index}`,
    category: CommandCategory.Effect,
    description: '',
    code: '',
  };
  return {...command, lastSavedState: command};
};

export const hasChanges = (command: SaveableCommand) => {
  if (!command.lastSavedState) {
    return true;
  }

  if (
    (command.name !== command.lastSavedState.name) ||
    (command.code !== command.lastSavedState.code) ||
    (command.author_id !== command.lastSavedState.author_id)
  ) {
    return true
  }

  return false;
}

const COMMAND_STORAGE_PREFIX = 'commands-feb-16-2021'

export const useCommandEditor = () => {
  const [commands, setCommands] = useStorageBackedState<SaveableCommand[]>([], `${COMMAND_STORAGE_PREFIX}-commands`);
  const [focusedCommandIndex, setFocusedCommandIndex] = useStorageBackedState<number | null>(0, `${COMMAND_STORAGE_PREFIX}-focused`);
  const [peekedCommandIndex, setPeekedCommandIndex] = useStorageBackedState<number | null>(0, `${COMMAND_STORAGE_PREFIX}-peeked`);

  const focusCommand = useCallback(
    (command: SaveableCommand | null) => {
      if (command === null) {
        setFocusedCommandIndex(null);
      } else if (commands.includes(command)) {
        setFocusedCommandIndex(commands.indexOf(command));
      } else {
        console.error('Command is not open');
        setFocusedCommandIndex(null);
      }
    },
    [commands, setFocusedCommandIndex]
  );

  const openCommand = useCallback(
    (command: SaveableCommand) => {
      const existingCommand = commands.find((c) => c.id === command.id);
      if (existingCommand) {
        setFocusedCommandIndex(commands.indexOf(existingCommand));
      } else {
        setCommands([...commands, command]);
        setFocusedCommandIndex(commands.length);
      }
    },
    [setCommands, commands, setFocusedCommandIndex]
  );

  const openSavedCommand = useCallback(
    (command: StoredCommand) => {
      const existingOpenedCommand = commands.find((c) => c.lastSavedState === command);
      if (existingOpenedCommand) {
        focusCommand(existingOpenedCommand);
      } else {
        openCommand({
          ...command,
          lastSavedState: command
        });
      }
    },
    [commands, openCommand, focusCommand]
  );

  const closeCommand = useCallback((command: SaveableCommand) => {
    let nextCommands = commands.filter((t) => t !== command);
    if (nextCommands.length === 0) {
      setPeekedCommandIndex(null);
      setFocusedCommandIndex(null);
    } else if (peekedCommandIndex === commands.indexOf(command)) {
      setPeekedCommandIndex(null);
    } else if (peekedCommandIndex !== null && peekedCommandIndex > commands.indexOf(command)) {
      setPeekedCommandIndex(peekedCommandIndex - 1);
    }

    if (nextCommands.length !== 0 && focusedCommandIndex && focusedCommandIndex >= commands.indexOf(command)) {
      setFocusedCommandIndex(focusedCommandIndex - 1);
    }

    setCommands(nextCommands);
  }, [setCommands, commands, focusedCommandIndex, peekedCommandIndex, setPeekedCommandIndex, setFocusedCommandIndex]);

  const focusedCommand = useMemo(() => {

    return (focusedCommandIndex !== null && commands[focusedCommandIndex]) || null;
  }, [commands, focusedCommandIndex]);

  const updateFocusedCommand = useCallback(
    (newCommandState: SaveableCommand) => {
      if (focusedCommandIndex === null) {
        return;
      }
      if (peekedCommandIndex === focusedCommandIndex) {
        setPeekedCommandIndex(null);
      }
      setCommands([
        ...commands.slice(0, focusedCommandIndex),
        newCommandState,
        ...commands.slice(focusedCommandIndex + 1)
      ]);
    },
    [setCommands, commands, peekedCommandIndex, focusedCommandIndex, setPeekedCommandIndex]
  );

  const peekCommand = useCallback(
    (command: StoredCommand) => {
      const savedCommand = { ...command, lastSavedState: command };
      if (peekedCommandIndex !== null && !!commands[peekedCommandIndex]) {
        setCommands([
          ...commands.slice(0, peekedCommandIndex),
          savedCommand,
          ...commands.slice(peekedCommandIndex + 1)
        ]);
        setFocusedCommandIndex(peekedCommandIndex);
      } else {
        openCommand(savedCommand);
        setPeekedCommandIndex(commands.length);
      }
    },
    [openCommand, setCommands, peekedCommandIndex, setFocusedCommandIndex, commands, setPeekedCommandIndex]
  );

  const lockPeekedCommand = useCallback(
    () => {
      setPeekedCommandIndex(null);
    },
    [setPeekedCommandIndex]
  );

  const peekedCommand = useMemo(() => peekedCommandIndex === null ? null : commands[peekedCommandIndex], [commands, peekedCommandIndex]);

  const createNew = useCallback(() => {
    openCommand(generateDefaultCommand(commands.length + 1));
  }, [commands, openCommand]);

  return {
    createNew,
    peekedCommand,
    lockPeekedCommand,
    commands,
    peekCommand,
    openCommand,
    openSavedCommand,
    closeCommand,
    setCommands,
    focusCommand,
    focusedCommand,
    updateFocusedCommand
  };
}

export const CommandEditorContext = React.createContext<ReturnType<typeof useCommandEditor>>(undefined as never);
