import { For, Match, Show, Switch } from "solid-js";
import { ParseError } from "src/lang/parser/parser";
import { SourceRegion } from "source-region";
import { ShowParseError } from 'src/ui/Component/ParseError';
import { Program } from "src/lang/program";

export type DigithError = {
  payload: DigithError.Payload,
  ids: DigithError.Id[],
  tags: DigithError.Tag[],
  config: DigithError.Config,
}

export namespace DigithError {
  export type Payload =
    | { tag: "Parse", err: ParseError, src: SourceRegion }
    | { tag: "Program", err: Program.Error };

  export type Id = string;
  export type Tag = string;

  export type Config = {
    title?: string,
    display?: "box" | "flat",
  }

  function findById(errors: DigithError[], id: Id): DigithError | undefined {
    return errors.find((e) => e.ids.includes(id));
  }

  function allWithTag(errors: DigithError[], tag: Tag): DigithError[] {
    return errors.filter((e) => e.tags.includes(tag));
  }

  export function All(props: { errors: DigithError[] }) {
    return (
      <div class="digith-errors-container">
        <For each={props.errors}>
          {(error) => <Single error={error} />}
        </For>
      </div>
    );
  }

  export function ById(props: { errors: DigithError[], id: Id }) {
    const error = () => findById(props.errors, props.id);
    return (
      <Show when={error()}>
        {(e) => <Single error={e()} />}
      </Show>
    );
  }

  export function ByTag(props: { errors: DigithError[], tag: Tag }) {
    const matched = () => allWithTag(props.errors, props.tag);
    return (
      <Show when={matched().length > 0}>
        <All errors={matched()} />
      </Show>
    );
  }

  function Single(props: { error: DigithError }) {
    const display = () => props.error.config.display ?? "box";
    return (
      <div style={{ "margin-bottom": display() === "box" ? "1rem" : "0.5rem" }} >
        <Switch>
          <Match when={display() === "box"}>
            <article style={{ border: "1px solid var(--pico-del-color)", padding: "0.5rem 1rem", margin: 0 }}>
              <Show when={props.error.config.title}>
                <header style={{ "margin-bottom": "0.5rem", color: "var(--pico-del-color)", "font-weight": "bold" }}>
                  {props.error.config.title}
                </header>
              </Show>
              <PayloadView payload={props.error.payload} />
            </article>
          </Match>

          <Match when={display() === "flat"}>
            <div style={{ color: "var(--pico-del-color)" }}>
              <Show when={props.error.config.title}>
                <small style={{ "font-weight": "bold", display: "block" }}>{props.error.config.title}:</small>
              </Show>
              <PayloadView payload={props.error.payload} />
            </div>
          </Match>
        </Switch>
      </div>
    );
  }

  function PayloadView(props: { payload: Payload }) {
    return (
      <Switch>
        <Match
          when={props.payload.tag === "Parse" ? (props.payload as Extract<Payload, { tag: "Parse" }>) : undefined}
        >
          {(err) => <ShowParseError text={err().src} err={err().err} />}
        </Match>

        <Match
          when={props.payload.tag === "Program" ? (props.payload as Extract<Payload, { tag: "Program" }>) : undefined}
        >
          {(err) => <ProgramErrorDisplay error={err().err} />}
        </Match>
      </Switch>
    );
  }

}

export function ProgramErrorDisplay(props: { error: Program.Error }) {
  const message = () => {
    switch (props.error.tag) {
      case "DuplicateFunctionName": 
        return `A function named '${props.error.name}' already exists.`;
      case "PrimitiveFunctionAlreadyExists":
        return `Cannot overwrite the primitive function '${props.error.name}'.`;
      // TODO: handle other cases
      default:
        return `Runtime Error: ${props.error.tag}`;
    }
  };

  return (
    <article style={{ border: "1px solid var(--pico-del-color)", padding: "0.5rem 1rem" }}>
      <small style={{ color: "var(--pico-del-color)", "font-weight": "bold" }}>
        Registration Failed
      </small>
      <p style={{ margin: 0 }}>{message()}</p>
    </article>
  );
}

