import { For, Show } from 'solid-js';
import type {
  ConcreteError,
  ConcreteErrorNode,
  PartialConcreteSyntax,
  PartialExpr,
  PartialList,
  PartialListItem,
} from '../../../languages/lisp';
import { LispExpr } from '../../../languages/lisp';
import { spanLabel } from '../../format';
import { HoverBlock } from '../../HoverBlock';
import { SpanChip } from '../../SpanChip';
import type { HoverTarget } from '../../types';
import { errorDetail, errorTitle } from './format';

export function StructureTree(props: {
  program: PartialConcreteSyntax;
  isValid: boolean;
  errorCount: number;
  onHover: (target: HoverTarget | undefined) => void;
}) {
  return (
    <div class="structure-tree">
      <HoverBlock
        class="syntax-node program-node"
        label="program"
        span={props.program.span}
        onHover={props.onHover}
      >
        <div class="node-header">
          <span class={props.isValid ? "status-dot status-valid" : "status-dot status-invalid"} />
          <span class="node-kind">program</span>
          <span class="item-meta">
            {props.isValid ? "valid" : "invalid"} · {props.program.expressions.length} expressions · {props.errorCount} errors
          </span>
        </div>

        <Show when={props.program.error}>
          {(error) => <ConcreteErrorView error={error()} label="program error" onHover={props.onHover} />}
        </Show>

        <div class="list-children">
          <For each={props.program.expressions}>
            {(expr, index) => (
              <ExprView
                expr={expr}
                label={`expression ${index() + 1}`}
                onHover={props.onHover}
              />
            )}
          </For>
        </div>
      </HoverBlock>
    </div>
  );
}

function ExprView(props: {
  expr: PartialExpr;
  label: string;
  onHover: (target: HoverTarget | undefined) => void;
}) {
  switch (props.expr.tag) {
    case "number":
    case "identifier":
      return (
        <HoverBlock
          class="syntax-node literal-node"
          label={`${props.label}: ${props.expr.tag}`}
          span={props.expr.span}
          onHover={props.onHover}
        >
          <div class="node-header">
            <span class="node-kind">{props.expr.tag}</span>
            <span class="node-value">{LispExpr.show(props.expr)}</span>
          </div>
        </HoverBlock>
      );

    case "error-number":
    case "error-identifier":
    case "error-expression":
      return (
        <HoverBlock
          class="syntax-node syntax-error-node"
          label={`${props.label}: ${props.expr.tag}`}
          span={props.expr.span}
          onHover={props.onHover}
        >
          <div class="node-header">
            <span class="status-dot status-invalid" />
            <span class="node-kind">{props.expr.tag}</span>
            <span class="item-meta">{spanLabel(props.expr.span)}</span>
          </div>
          <ConcreteErrorView error={props.expr.error} label={props.expr.tag} onHover={props.onHover} />
        </HoverBlock>
      );

    case "list":
      return <ListView list={props.expr} label={props.label} onHover={props.onHover} />;
  }
}

function ListView(props: {
  list: PartialList;
  label: string;
  onHover: (target: HoverTarget | undefined) => void;
}) {
  return (
    <HoverBlock
      class={props.list.error ? "syntax-node list-node syntax-error-node" : "syntax-node list-node"}
      label={`${props.label}: list`}
      span={props.list.span}
      onHover={props.onHover}
    >
      <div class="node-header">
        <Show when={props.list.error}>
          <span class="status-dot status-invalid" />
        </Show>
        <span class="node-kind">{listLabel(props.list.open.tag)}</span>
        <span class="item-meta">{props.list.items.length} items · {delimiterLabel(props.list)}</span>
      </div>

      <div class="delimiter-row">
        <SpanChip label={props.list.open.tag} span={props.list.open.span} onHover={props.onHover} />
        <Show when={props.list.close}>
          {(close) => <SpanChip label={close().tag} span={close().span} onHover={props.onHover} />}
        </Show>
      </div>

      <Show when={props.list.error}>
        {(error) => <ConcreteErrorView error={error()} label="list error" onHover={props.onHover} />}
      </Show>

      <div class="list-children">
        <For each={props.list.items}>
          {(item, index) => (
            <ListItemView
              item={item}
              label={`${props.label}.${index() + 1}`}
              onHover={props.onHover}
            />
          )}
        </For>
      </div>
    </HoverBlock>
  );
}

function ListItemView(props: {
  item: PartialListItem;
  label: string;
  onHover: (target: HoverTarget | undefined) => void;
}) {
  if (props.item.tag === "error-list-separator") {
    return (
      <HoverBlock
        class="syntax-node syntax-error-node"
        label={`${props.label}: separator`}
        span={props.item.span}
        onHover={props.onHover}
      >
        <div class="node-header">
          <span class="status-dot status-invalid" />
          <span class="node-kind">error-list-separator</span>
          <span class="item-meta">{spanLabel(props.item.span)}</span>
        </div>
        <ConcreteErrorView error={props.item.error} label="separator error" onHover={props.onHover} />
      </HoverBlock>
    );
  }

  return <ExprView expr={props.item} label={props.label} onHover={props.onHover} />;
}

function ConcreteErrorView(props: {
  error: ConcreteError;
  label: string;
  onHover: (target: HoverTarget | undefined) => void;
}) {
  return (
    <div class="concrete-error-list">
      <For each={props.error}>
        {(node, index) => (
          <ConcreteErrorNodeView
            node={node}
            label={`${props.label} ${index() + 1}`}
            onHover={props.onHover}
          />
        )}
      </For>
    </div>
  );
}

function ConcreteErrorNodeView(props: {
  node: ConcreteErrorNode;
  label: string;
  onHover: (target: HoverTarget | undefined) => void;
}) {
  return (
    <div class="concrete-error">
      <div class="error-title">{errorTitle(props.node.error)}</div>
      <div class="item-meta">{errorDetail(props.node.error)}</div>
      <div class="span-chip-row">
        <SpanChip label="focus" span={props.node.span} onHover={props.onHover} />
        <Show when={props.node.panickedOver}>
          {(span) => <SpanChip label="panicked over" span={span()} onHover={props.onHover} />}
        </Show>
      </div>
    </div>
  );
}

function listLabel(tag: string): string {
  return tag === "open-bracket" ? "square-list" : "round-list";
}

function delimiterLabel(list: PartialList): string {
  return list.close ? `${list.open.tag} / ${list.close.tag}` : `${list.open.tag} / missing close`;
}
