import { useContext, useEffect, useRef } from "react";

import { ExprBlock, type ExprBlockState } from "./ExprBlock";
import { EnvContext } from "./EnvContext";
import { evalEditorBlock, makeInnerEnv, scoreResolved, type ResolvedType } from "./eval";
import { type State2Props } from "./ExprBlock";
import { autoInputWidth } from "./util/dom_trickery";
import { GlobalContext } from "./GlobalContext";

import "./LetInBlock.css";

export interface LetInBlockState {
  kind: "let";
  name: string;
  value: ExprBlockState;
  inner: ExprBlockState;
}

interface LetInBlockProps extends State2Props<LetInBlockState,ExprBlockState> {}

export function LetInBlock(props: LetInBlockProps) {
  return <span className="letIn">
    <div className="decl">
      <DeclColumns {...props} />
    </div>
    <div className="inner">
      <InnerMost {...props} />
    </div>
  </span>
}

function DeclColumns({state: {name, value, inner}, setState, suggestionPriority}) {
  const env = useContext(EnvContext);
  const globalContext = useContext(GlobalContext);

  const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
  const setValue = callback => setState(state => ({...state, value: callback(state.value)}));
  const onChangeName = (e: React.ChangeEvent<HTMLInputElement>) => {
    setState(state => ({...state, name: e.target.value}));
  }

  const valueSuggestionPriority = (suggestion: ResolvedType) => {
    const innerEnv = makeInnerEnv(env, name, suggestion);
    const resolved = evalEditorBlock(inner, innerEnv);
    return scoreResolved(resolved, suggestionPriority);
  };

  const nameRef = useRef<HTMLInputElement>(null);
  useEffect(() => {
    nameRef.current?.focus();
  }, []);
  useEffect(() => autoInputWidth(nameRef, name, 60), [nameRef, name]);

  const valueResolved = evalEditorBlock(value, env);
  const innerEnv = makeInnerEnv(env, name, valueResolved);

  return <>
    <span className="keyword column">let&nbsp;</span>
      <span className="column rightAlign">
      <input
        ref={nameRef}
        className='editable'
        value={name}
        placeholder="<name>"
        onChange={onChangeName}
        />
      </span>
      <span className="keyword column">&nbsp;=&nbsp;</span>
      <span className="column">
        <ExprBlock
          state={value}
          setState={setValue}
          suggestionPriority={valueSuggestionPriority}
          onCancel={() => setState(state => state.inner)} // keep inner
          />
      </span>
    {inner.kind === "let" &&
     globalContext?.syntacticSugar &&
      <EnvContext value={innerEnv}>
        <DeclColumns
          state={inner}
          setState={setInner}
          suggestionPriority={suggestionPriority}
          />
      </EnvContext>
    }
  </>;
}

function InnerMost({state, setState, suggestionPriority}) {
  const env = useContext(EnvContext);
  const globalContext = useContext(GlobalContext);
  const setInner = callback => setState(state => ({...state, inner: callback(state.inner)}));
  const valueResolved = evalEditorBlock(state.value, env);
  const innerEnv = makeInnerEnv(env, state.name, valueResolved);
  const onCancel = () => setState(state => state.value);
  if (state.inner.kind === "let" && globalContext?.syntacticSugar) {
    return <EnvContext value={innerEnv}>
        <InnerMost
          state={state.inner}
          setState={setInner}
          suggestionPriority={suggestionPriority}
          />
      </EnvContext>;
  }
  else {
    return <EnvContext value={innerEnv}>
      <ExprBlock
        state={state.inner}
        setState={setInner}
        suggestionPriority={suggestionPriority}
        onCancel={onCancel} // keep value
        />
    </EnvContext>
  }
}