import { createEffect, createSignal, onCleanup, Show } from "solid-js";
import { Digith } from "src/ui/Digith";
import { useProgram } from "src/ui/ProgramProvider";
import { CodeEditor } from "src/ui/Component/CodeEditor";
import { sourceText } from "source-region";
import { Program } from "src/lang/program";
import { V, Validation, letValidate } from "src/ui/validation";
import { validateSignalExprRaw } from "src/ui/validation/helpers";
import { updateDigith } from "src/ui/Scrowl/scrowlStore";
import { DigithError } from "src/ui/Digith/DigithError";
import { Value } from "src/lang/eval/value";
import { Val } from "src/ui/Component/Value";
import { externalSubscribe } from "src/lang/eval/signalValue";

type Input = {
  raw_body: string,
}

const validator: Validation<Input, Program.UpdateSignal, DigithError> = letValidate(
  (input) => ({
    body: V.elseErr(validateSignalExprRaw(input.raw_body), err => ({
      payload: { tag: "Parse", field: "body", err, src: sourceText(input.raw_body).fullRegion() },
      ids: ["body"],
      tags: ["footer"],
      config: { title: "Signal Body" },
    })),
  }),
  (fields, input) => {
    return V.ok({ 
      body: fields.body,
      raw_body: input.raw_body 
    });
  }
);

export function SignalDigith(props: { signal: Digith.Signal }) {
  const program = useProgram();

  const [value, setValue] = createSignal<Value | null>(null);

  const [body, setBody] = createSignal(props.signal.raw_body);

  const [errors, setErrors] = createSignal<DigithError[]>([]);

  const isDirty = () => 
    body() !== props.signal.raw_body;

  createEffect(() => {
    // TODO: Improve runtime error view
    try {
      const signal = Program.get_or_create_signal(program, props.signal.name);

      // TODO: Not sure about this one. Setting a signal in `setValue` is discouraged.
      setTimeout(() => setValue(signal.currentValue), 0);

      const cancel = externalSubscribe(signal, (newValue) => {
        setValue(newValue);
      });
      onCleanup(cancel);
    } catch (e) {
      // TODO: setErrors... but how? Shouldn't this be independent of `errors`?
      console.log("Failed to link Signal: ", e);
    }
  });

  function handleRedefine() {
    setErrors([]);

    const validRes = validator({ raw_body: body() });
    if (validRes.tag === "errors") {
      setErrors(validRes.errors);
      return;
    }
    const updateData = validRes.value;

    const progRes = Program.updateSignal(program, props.signal.name, {
      body: updateData.body,
      raw_body: updateData.raw_body
    });

    if (progRes.tag === "error") {
      setErrors([{
        payload: { tag: "Program", err: progRes.error },
        ids: ["program"],
        tags: ["footer"],
        config: { title: "Update Failed" },
      }]);
      return;
    }

    // reloading the digith
    updateDigith(props.signal.id, {
      ...props.signal,
      raw_body: updateData.raw_body
    });
  }
  // TODO
  return (
    <article>
      <header>
        <strong>Signal</strong>

        {/* Dirty Indicator / Status */}
        <div>
           <Show when={isDirty()} fallback={<span style={{color: "var(--pico-muted-color)"}}>Synced</span>}>
             <span style={{color: "var(--pico-primary)"}}>● Unsaved Changes</span>
           </Show>
        </div>
      </header>

      <div>
        Name
        <input 
          type="text" 
          value={props.signal.name} 
          disabled 
          style={{ opacity: 0.7, cursor: "not-allowed" }}
        />
      </div>

      <label>Body</label>
      <CodeEditor 
        value={body()} 
        onUpdate={setBody} 
        onRun={handleRedefine} 
      />

      <footer style={{ display: "flex", "align-items": "center", gap: "1rem" }}>
        <button 
          onClick={handleRedefine}
          disabled={!isDirty()}
        >
          Redefine
        </button>

      </footer>

      <div style={{ "margin-top": "1rem" }}>
        <DigithError.ByTag errors={errors()} tag="footer" />
      </div>

     <Show when={ value() } fallback={<div>Initializing...</div>}>
       {(value) => (
         <div>
           <label>Current Value</label>
           <Val value={ value() } />
         </div>
       )}
     </Show>
    </article>
  );
}

