import { useEffect, useState } from 'react';
import './App.css';
import { CommandContext } from './CommandContext';
import { Editor, type EditorState } from './Editor';
import { extendedEnv } from './EnvContext';
import { biggerExample, initialEditorState, lambda2Params, nonEmptyEditorState, tripleFunctionCallEditorState } from "./configurations";
import { evalEditorBlock } from "./eval";

const commands: [string, string[], string][] = [
  ["call"     , ['c'              ], "call"          ],
  ["eval"     , ['e','Tab','Enter'], "eval"          ],
  ["transform", ['t', '.'         ], "transform"     ],
  ["let"      , ['l', '='         ], "let … in …"    ],
  ["lambda"   , ['a'              ], "λx: …"         ],
];

const examples: [string, EditorState][] = [
  ["empty editor", initialEditorState],
  ["push to list", nonEmptyEditorState],
  ["function w/ 4 params", tripleFunctionCallEditorState],
  ["bigger example", biggerExample],
  ["lambda 2 params", lambda2Params],
];

type AppState = {
  history: EditorState[],
  future: EditorState[],
}

const defaultState = {
  history: [initialEditorState],
  future: [],
};

function loadFromLocalStorage(): AppState {
  if (localStorage["appState"]) {
    try {
      const appState = JSON.parse(localStorage["appState"]); // may throw
      // if our state is corrupt, discover it eagerly:
      evalEditorBlock(appState.history.at(-1), extendedEnv);

      return appState; // all good
    }
    catch (e) {
      console.log('error recovering state from localStorage (resetting):', e);
    }
  }
  return defaultState;
}

export function App() {
  // load from localStorage
  const [appState, setAppState] = useState(loadFromLocalStorage());

  const [syntacticSugar, setSyntacticSugar] = useState(true);

  useEffect(() => {
    // persist accross reloads
    localStorage["appState"] = JSON.stringify(appState);
  }, [appState]);

  const factoryReset = () => {
    setAppState(_ => defaultState);
  }

  const pushHistory = (callback: (p: EditorState) => EditorState) => {
    setAppState(({history}) => {
      const newState = callback(history.at(-1)!);
      return {
        history: history.concat([newState]),
        future: [],
      };
    });
  };

  const onUndo = () => {
    setAppState(({history, future}) => ({
      history: history.slice(0,-1),
      future: future.concat(history.at(-1)!),
    }));
  };
  const onRedo = () => {
    setAppState(({history, future}) => ({
      history: history.concat(future.at(-1)!),
      future: future.slice(0,-1),
    }));
  };

  const onKeyDown = (e) => {
    if (e.key === "Z" && e.ctrlKey) {
      if (e.shiftKey) {
        if (appState.future.length > 0) {
          onRedo();
        }
      }
      else {
        if (appState.history.length > 1) {
          onUndo();
        }
      }
      e.preventDefault();
    }
  };

  useEffect(() => {
    window['APP_STATE'] = appState.history; // useful for debugging
  }, [appState.history]);

  useEffect(() => {
    window.onkeydown = onKeyDown;
  }, []);

  const [highlighted, setHighlighted] = useState(
    commands.map(() => false));

  const doHighlight = Object.fromEntries(commands.map(([id], i) => {
    return [id, () => {
      setHighlighted(h => h.with(i, true));
      setTimeout(() => setHighlighted(h => h.with(i, false)), 100);
    }];
  }));

  const onSelectExample = (e: React.SyntheticEvent<HTMLSelectElement>) => {
    // @ts-ignore
    if (e.target.value >= 0) {
      // @ts-ignore
      // @ts-ignore
      pushHistory(_ => examples[e.target.value][1]);
    }
  }

  return (
    <>
      <header>
        <button disabled={appState.history.length===1} onClick={onUndo}>Undo ({appState.history.length-1}) <kbd>Ctrl</kbd>+<kbd>Z</kbd></button>
        <button disabled={appState.future.length===0} onClick={onRedo}>Redo ({appState.future.length})  <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd></button>
        {
          commands.map(([_, keys, descr], i) =>
            <span key={i} className={'command' + (highlighted[i] ? (' highlighted') : '')}>
              {keys.map((key, j) => <kbd key={j}>{key}</kbd>)}
              {descr}
            </span>)
        }
        <select onChange={onSelectExample} value={-1}>
          <option value={-1}>load example...</option>
          {
            examples.map(([name], i) => {
              return <option key={i} value={i}>{name}</option>;
            })
          }
        </select>
        <button className="factoryReset" onClick={factoryReset}>
          FACTORY RESET
        </button>
        <label>
        <input type="checkbox"
          checked={syntacticSugar}
          onChange={e => setSyntacticSugar(e.target.checked)}/>
        syntactic sugar
        </label>
      </header>

      <main onKeyDown={onKeyDown}>
        <CommandContext value={{undo: onUndo, redo: onRedo, doHighlight, syntacticSugar}}>
          <Editor
            state={appState.history.at(-1)!}
            setState={pushHistory}
            onCancel={() => {}}
            suggestionPriority={() => 1}
          />
        </CommandContext>


      </main>

      <footer>
        <a href="https://deemz.org/git/joeri/dope2-webapp">Source code</a>
      </footer>
    </>
  )
}
