<script lang="ts">
  import EntryView from "./EntryView.svelte";
  import { query, useEntity } from "../lib/entity";
  import UpObject from "./display/UpObject.svelte";
  import { createEventDispatcher, setContext } from "svelte";
  import { writable } from "svelte/store";
  import type { UpEntry } from "upend";
  import Spinner from "./utils/Spinner.svelte";
  import NotesEditor from "./utils/NotesEditor.svelte";
  import type { AttributeChange } from "../types/base";
  import Selector from "./utils/Selector.svelte";
  import type { IValue } from "upend/types";
  import IconButton from "./utils/IconButton.svelte";
  import type { BrowseContext } from "../util/browse";
  import { Link, useParams } from "svelte-navigator";
  import Icon from "./utils/Icon.svelte";
  import BlobViewer from "./display/BlobViewer.svelte";
  import { i18n } from "../i18n";
  import EntryList from "./widgets/EntryList.svelte";
  import api from "../lib/api";
  import Gallery from "./widgets/Gallery.svelte";
  const dispatch = createEventDispatcher();
  const params = useParams();

  export let address: string;
  export let index: number | undefined;
  export let detail: boolean;
  export let editable = false;
  let showAsEntries = false;
  let highlightedType: string | undefined;

  let blobHandled = false;

  let indexStore = writable(index);
  $: $indexStore = index;

  let addressesStore = writable([]);
  $: $addressesStore = $params.addresses?.split(",") || [];

  setContext("browse", {
    index: indexStore,
    addresses: addressesStore,
  } as BrowseContext);

  $: ({ entity, entityInfo, error, revalidate } = useEntity(address));

  let typedAttributes = {} as { [key: string]: UpEntry[] };
  let untypedAttributes = [] as UpEntry[];

  $: {
    typedAttributes = {};
    untypedAttributes = [];

    ($entity?.attributes || []).forEach((entry) => {
      const entryTypes = Object.entries(/*allTypes*/ {}).filter(
        ([_, t]) =>
          // t.attributes.includes(entry.attribute)
          false
      );
      if (entryTypes.length > 0) {
        entryTypes.forEach(([addr, _]) => {
          if (typedAttributes[addr] == undefined) {
            typedAttributes[addr] = [];
          }
          typedAttributes[addr].push(entry);
        });
      } else {
        untypedAttributes.push(entry);
      }
    });

    typedAttributes = typedAttributes;
    untypedAttributes = untypedAttributes;
  }

  $: filteredUntypedAttributes = untypedAttributes.filter(
    (entry) =>
      ![
        "LBL",
        "OF",
        "NOTE",
        "LAST_VISITED",
        "NUM_VISITED",
        "LAST_ATTRIBUTE_WIDGET",
      ].includes(entry.attribute)
  );

  $: currentUntypedAttributes = editable
    ? untypedAttributes
    : filteredUntypedAttributes;

  $: currentBacklinks =
    (editable
      ? $entity?.backlinks
      : $entity?.backlinks.filter(
          (entry) => !["OF"].includes(entry.attribute)
        )) || [];

  $: groups = ($entity?.attr["OF"] || []).map((e) => e.value.c as string);
  $: tagged = $entity?.attr["~OF"] || [];

  let attributesUsed: UpEntry[] = [];
  $: {
    if ($entityInfo?.t === "Attribute") {
      api
        .query(`(matches ? "${$entityInfo.c}" ?)`)
        .then((result) => (attributesUsed = result.entries));
    }
  }

  async function onChange(ev: CustomEvent<AttributeChange>) {
    const change = ev.detail;
    switch (change.type) {
      case "create":
        await api.putEntry({
          entity: address,
          attribute: change.attribute,
          value: change.value,
        });
        break;
      case "delete":
        await api.deleteEntry(change.address);
        break;
      case "update":
        await api.putEntityAttribute(address, change.attribute, change.value);
        break;
      default:
        console.error("Unimplemented AttributeChange", change);
        return;
    }
    revalidate();
  }

  let identities = [address];
  function onResolved(ev: CustomEvent<string[]>) {
    identities = ev.detail;
    dispatch("resolved", ev.detail);
  }

  let groupToAdd: IValue | undefined;
  async function addGroup() {
    if (!groupToAdd) {
      return;
    }
    await api.putEntry([
      {
        entity: address,
        attribute: "OF",
        value: {
          t: "Address",
          c: String(groupToAdd.c),
        },
      },
    ]);
    revalidate();
    groupToAdd = undefined;
  }

  async function removeGroup(groupAddress: string) {
    await api.deleteEntry(groupAddress);
    revalidate();
  }

  async function deleteObject() {
    if (confirm(`${$i18n.t("Really delete")} "${identities.join(" | ")}"?`)) {
      await api.deleteEntry(address);
      dispatch("close");
    }
  }

  async function onAttributeWidgetSwitch(ev: CustomEvent<string>) {
    await api.putEntityAttribute(address, "LAST_ATTRIBUTE_WIDGET", {
      t: "String",
      c: ev.detail,
    });
  }

  $: entity.subscribe(async (object) => {
    if (object && object.listing.entries.length) {
      await api.putEntityAttribute(object.address, "LAST_VISITED", {
        t: "Number",
        c: new Date().getTime() / 1000,
      });

      await api.putEntityAttribute(object.address, "NUM_VISITED", {
        t: "Number",
        c: (parseInt(String(object.get("NUM_VISITED"))) || 0) + 1,
      });
    }
  });
</script>

<div class="inspect" class:detail class:blob={blobHandled}>
  <header>
    <h2>
      {#if $entity}
        <UpObject banner {address} on:resolved={onResolved} />
      {:else}
        <Spinner centered />
      {/if}
    </h2>
  </header>
  {#if !showAsEntries}
    <div class="main-content">
      <div class="detail-col">
        <div class="blob-viewer">
          <BlobViewer
            {address}
            {editable}
            {detail}
            on:handled={(ev) => (blobHandled = ev.detail)}
          />
        </div>
        <NotesEditor {address} {editable} on:change={onChange} />
        {#if !$error}
          {#if groups.length}
            <section class="tags labelborder">
              <header><h3>{$i18n.t("Tags")}</h3></header>
              <div class="content">
                {#each groups as group}
                  <div
                    class="tag"
                    on:mouseenter={() => (highlightedType = group)}
                    on:mouseleave={() => (highlightedType = undefined)}
                  >
                    <UpObject address={group} link />
                    {#if editable}
                      <IconButton name="x-circle" />
                    {/if}
                  </div>
                {/each}
                {#if editable}
                  <div class="selector">
                    <Selector
                      type="value"
                      valueTypes={["Address"]}
                      bind:value={groupToAdd}
                      on:input={addGroup}
                      placeholder="Choose an entity..."
                    />
                  </div>
                {/if}
              </div>
            </section>
          {/if}
          <div class="attributes">
            {#each Object.entries(typedAttributes) as [typeAddr, entries] (typeAddr)}
              <EntryView
                {entries}
                {editable}
                on:change={onChange}
                initialWidget={String($entity.get("LAST_ATTRIBUTE_WIDGET"))}
                on:widgetSwitched={onAttributeWidgetSwitch}
                highlighted={highlightedType == typeAddr}
              />
            {/each}

            {#if currentUntypedAttributes.length > 0 || editable}
              <EntryView
                title={$i18n.t("Attributes")}
                {editable}
                entries={currentUntypedAttributes}
                on:change={onChange}
              />
            {/if}

            {#if currentBacklinks.length > 0}
              <EntryView
                title={`${$i18n.t("Referred to")} (${currentBacklinks.length})`}
                entries={currentBacklinks}
                on:change={onChange}
              />
            {/if}

            {#if tagged.length > 0}
              <EntryView
                title={`${$i18n.t("Links")}`}
                widgets={[
                  {
                    name: "List",
                    components: (entries) => [
                      {
                        component: Gallery,
                        props: {
                          entities: entries.map((e) => e.entity),
                          thumbnails: false,
                        },
                      },
                    ],
                  },
                  {
                    name: "Gallery",
                    components: (entries) => [
                      {
                        component: Gallery,
                        props: {
                          entities: entries.map((e) => e.entity),
                          thumbnails: true,
                        },
                      },
                    ],
                  },
                ]}
                entries={tagged}
                on:change={onChange}
              />
            {/if}

            {#if $entityInfo?.t === "Attribute"}
              <div class="buttons">
                <div class="button">
                  <Link to="/surface?x={$entityInfo.c}">
                    {$i18n.t("Surface view")}
                  </Link>
                </div>
              </div>

              <section class="labelborder">
                <header>
                  <h3>{$i18n.t("Used")} ({attributesUsed.length})</h3>
                </header>
                <EntryList
                  columns="entity,value"
                  columnWidths={["auto", "33%"]}
                  entries={attributesUsed}
                  orderByValue
                />
              </section>
            {/if}
          </div>

          {#if editable}
            <!-- svelte-ignore a11y-click-events-have-key-events -->
            <div class="button" on:click={deleteObject}>
              <Icon name="trash" />
            </div>
          {/if}
        {:else}
          <div class="error">
            {$error}
          </div>
        {/if}
      </div>
    </div>
  {:else}
    <div class="entries">
      <h2>{$i18n.t("Attributes")}</h2>
      <EntryList
        {editable}
        entries={$entity.attributes}
        columns={detail
          ? "timestamp, provenance, attribute, value"
          : "attribute, value"}
      />
      <h2>{$i18n.t("Backlinks")}</h2>
      <EntryList
        entries={$entity.backlinks}
        columns={detail
          ? "timestamp, provenance, entity, attribute"
          : "entity, attribute"}
      />
    </div>
  {/if}
  <div class="footer">
    <IconButton
      name="detail"
      title="Show as entries"
      active={showAsEntries}
      on:click={() => (showAsEntries = !showAsEntries)}
    />
  </div>
</div>

<style scoped lang="scss">
  @use "./util";

  header h2 {
    margin-bottom: 0;
  }

  .inspect,
  .main-content {
    flex: auto;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-height: 0;
  }

  .tags {
    margin: 0.25rem 0;
    .content {
      display: flex;
      flex-wrap: wrap;
      gap: 0.5rem 0.5rem;
      align-items: center;
    }

    .tag {
      display: inline-flex;
      align-items: center;
    }

    .selector {
      width: 100%;
    }
  }

  .attributes {
    flex: auto;
    height: 0; // https://stackoverflow.com/a/14964944
    min-height: 12em;
    overflow-y: auto;
  }

  @media screen and (max-height: 1080px) {
    .main-content {
      overflow-y: auto;
      // min-height: 0;
    }

    .attributes {
      height: unset;
      min-height: unset;
      overflow-y: unset;
    }
  }

  .inspect.detail {
    .main-content {
      position: relative;
      flex-direction: row;
      justify-content: end;
    }

    .blob-viewer {
      width: 73%;
      height: 100%;
      position: absolute;
      left: 1%;
      top: 0;
    }

    .detail-col {
      width: 25%;
    }

    &.blob {
      .detail-col {
        flex-grow: 0;
      }
    }
  }

  .main-content .detail-col {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
  }

  .entries {
    flex-grow: 1;
  }

  .footer {
    display: flex;
    justify-content: end;
  }

  .buttons {
    display: flex;
  }

  .error {
    color: red;
  }
</style>
