<script lang="ts">
  import { readable, type Readable } from "svelte/store";
  import type { UpListing } from "upend";
  import type { Address } from "upend/types";
  import { query } from "../../lib/entity";
  import UpObject from "../display/UpObject.svelte";
  import UpObjectCard from "../display/UpObjectCard.svelte";
  import { ATTR_LABEL } from "upend/constants";

  export let entities: Address[];
  export let thumbnails = true;
  export let sort = true;

  const deduplicatedEntities = Array.from(new Set(entities));

  let style: "list" | "grid" | "flex" = "grid";

  let clientWidth: number;
  $: style = !thumbnails || clientWidth < 600 ? "list" : "grid";

  // Sorting
  let sortedEntities = [];
  let resort = false;

  let sortKeys: { [key: string]: string[] } = {};
  function addSortKeys(key: string, vals: string[]) {
    if (!sortKeys[key]) {
      sortKeys[key] = [];
    }
    let changed = false;
    vals.forEach((val) => {
      if (!sortKeys[key].includes(val)) {
        changed = true;
        sortKeys[key].push(val);
      }
    });

    if (resort && changed) sortEntities();
  }

  // Labelling
  let labelListing: Readable<UpListing> = readable(undefined);
  $: {
    const addresses = [];
    deduplicatedEntities.forEach((addr) => {
      if (!addresses.includes(addr)) {
        addresses.push(addr);
      }
    });

    const addressesString = addresses.map((addr) => `@${addr}`).join(" ");

    labelListing = query(
      `(matches (in ${addressesString}) "${ATTR_LABEL}" ? )`
    ).result;
  }

  function sortEntities() {
    if (!sort) return;

    sortedEntities = deduplicatedEntities.concat();

    sortedEntities.sort((a, b) => {
      if (!sortKeys[a]?.length || !sortKeys[b]?.length) {
        if (Boolean(sortKeys[a]?.length) && !sortKeys[b]?.length) {
          return -1;
        } else if (!sortKeys[a]?.length && Boolean(sortKeys[b]?.length)) {
          return 1;
        } else {
          return a.localeCompare(b);
        }
      } else {
        return sortKeys[a][0].localeCompare(sortKeys[b][0], undefined, {
          numeric: true,
        });
      }
    });
  }

  $: {
    if ($labelListing) {
      deduplicatedEntities.forEach((address) => {
        addSortKeys(address, $labelListing.getObject(address).identify());
      });
      sortEntities();
      resort = true;
    }
  }

  if (!sort) {
    sortedEntities = entities;
  }
</script>

<div
  class="gallery style-{style}"
  class:has-thumbnails={thumbnails}
  bind:clientWidth
>
  <div class="header" />
  <div class="items">
    {#each sortedEntities as entity (entity)}
      {#if thumbnails}
        <UpObjectCard
          address={entity}
          labels={sortKeys[entity]}
          banner={false}
          on:resolved={(event) => {
            addSortKeys(entity, event.detail);
          }}
        />
      {:else}
        <UpObject
          link
          address={entity}
          labels={sortKeys[entity]}
          on:resolved={(event) => {
            addSortKeys(entity, event.detail);
          }}
        />
      {/if}
    {/each}
  </div>
</div>

<style lang="scss">
  .items {
    gap: 4px;
  }

  .gallery.has-thumbnails .items {
    gap: 1rem;
  }

  :global(.gallery.style-grid .items) {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
  }

  :global(.gallery.style-flex .items) {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
  }

  :global(.gallery.style-list .items) {
    display: flex;
    flex-direction: column;
    align-items: stretch;
  }
</style>
