<script lang="ts">
  /*** IMPORT ------------------------------------------- ***/
  import {
    isInputObjectType,
    isInterfaceType,
    isObjectType,
    type GraphQLField,
    type GraphQLInputField,
    type GraphQLNamedType,
    type GraphQLSchema
  } from "graphql";

  /*** UTILITY ------------------------------------------ ***/
  import FieldView from "./DocExplorer/FieldView.svelte";
  import TypeView from "./DocExplorer/TypeView.svelte";

  type NavEntry =
    | { kind: "field"; fieldName: string; typeName: string }
    | { kind: "type"; name: string };

  type Props = {
    schema: GraphQLSchema;
  };

  let { schema }: Props = $props();
  let stack = $state<NavEntry[]>([]);

  const current = $derived<NavEntry | null>(stack.length > 0 ? stack[stack.length - 1] : null);

  const rootTypes = $derived.by(() => {
    const out: { label: string; type: GraphQLNamedType }[] = [];
    const q = schema.getQueryType();
    const m = schema.getMutationType();
    const s = schema.getSubscriptionType();

    if (q)
      out.push({ label: "Query", type: q });

    if (m)
      out.push({ label: "Mutation", type: m });

    if (s)
      out.push({ label: "Subscription", type: s });

    return out;
  });

  const currentType = $derived.by<GraphQLNamedType | null>(() => {
    if (!current)
      return null;

    const name = current.kind === "type" ? current.name : current.typeName;
    return schema.getType(name) ?? null;
  });

  const currentField = $derived.by<GraphQLField<unknown, unknown> | GraphQLInputField | null>(() => {
    if (!current || current.kind !== "field" || !currentType)
      return null;

    if (isObjectType(currentType) || isInterfaceType(currentType) || isInputObjectType(currentType))
      return currentType.getFields()[current.fieldName] ?? null;

    return null;
  });

  /*** HELPER ------------------------------------------- ***/
  function crumbLabel(entry: NavEntry): string {
    return entry.kind === "type" ? entry.name : entry.fieldName;
  }

  function gotoIndex(index: number) {
    stack = stack.slice(0, index + 1);
  }

  function gotoRoot() {
    stack = [];
  }

  function pushField(fieldName: string) {
    const typeName = current?.kind === "type" ? current.name : null;

    if (!typeName)
      return;

    stack = [...stack, { fieldName, kind: "field", typeName }];
  }

  function pushType(name: string) {
    if (!schema.getType(name))
      return;

    stack = [...stack, { kind: "type", name }];
  }
</script>

<style lang="scss">
  .explorer {
    background-color: color-mix(in oklch shorter hue, var(--uchu-gray-1) 20%, var(--uchu-yang) 80%);
    border-left: 1px solid var(--uchu-gray-2);
    display: grid;
    grid-template-rows: auto 1fr;
    height: 100%;
    min-height: 0;
    overflow: hidden;
    z-index: 1;
  }

  .breadcrumbs {
    align-items: center;
    border-bottom: 1px solid var(--uchu-gray-2);
    display: flex;
    flex-wrap: wrap;
    font-size: 0.8125rem;
    gap: 0.25rem;
    padding: 0.5rem 0.75rem;

    .crumb {
      background: none;
      border: none;
      cursor: pointer;
      font-family: inherit;
      font-size: inherit;
      padding: 0;

      &:not(.current) {
        color: var(--uchu-blue-3);
        text-decoration: underline;
      }

      &.current {
        color: var(--uchu-yin-4);
        cursor: default;

        &:hover {
          text-decoration: none;
        }
      }
    }
  }

  .separator {
    color: var(--uchu-gray-3);
  }

  .body {
    min-height: 0;
    overflow-y: auto;
  }

  .root {
    display: grid;
    gap: 0.75rem;
    padding: 0.75rem 1rem;

    &-list {
      display: grid;
      gap: 0.375rem;
    }

    &-link {
      background: none;
      border: none;
      color: var(--uchu-blue-3);
      cursor: pointer;
      font-family: inherit;
      font-size: 0.95rem;
      padding: 0;
      text-align: left;

      &:hover {
        text-decoration: underline;
      }
    }
  }

  .section-label {
    color: var(--uchu-yin-6);
    font-size: 0.7rem;
    letter-spacing: 0.05rem;
    margin-bottom: 0.25rem;
    text-transform: uppercase;
  }

  .empty {
    color: var(--uchu-yin-6);
    font-size: 0.8rem;
    padding: 0.75rem 1rem;
  }
</style>

<div class="explorer">
  <div class="breadcrumbs">
    <button
      class="crumb"
      class:current={stack.length === 0}
      onclick={gotoRoot}>Docs</button>
    {#each stack as entry, i}
      <span class="separator">/</span>
      <button
        class="crumb"
        class:current={i === stack.length - 1}
        onclick={() => gotoIndex(i)}>{crumbLabel(entry)}</button>
    {/each}
  </div>

  <div class="body">
    {#if stack.length === 0}
      <div class="root">
        <div class="section-label">Root Types</div>

        <div class="root-list">
          {#each rootTypes as entry}
            <button class="root-link" onclick={() => pushType(entry.type.name)}>
              {entry.label}: {entry.type.name}
            </button>
          {/each}
        </div>
      </div>
    {:else if currentField}
      <FieldView field={currentField} onNavigate={pushType}/>
    {:else if currentType}
      <TypeView
        onNavigateField={pushField}
        onNavigateType={pushType}
        type={currentType}/>
    {:else}
      <div class="empty">Type not found in schema.</div>
    {/if}
  </div>
</div>
