<script>
  import { flip } from "svelte/animate"
  import { dndzone } from "svelte-dnd-action"
  import {
    Icon,
    Button,
    Layout,
    DrawerContent,
    ActionButton,
    Search,
  } from "@budibase/bbui"
  import { getAvailableActions } from "./index"
  import { generate } from "shortid"
  import { getEventContextBindings } from "builderStore/dataBinding"
  import { currentAsset, store } from "builderStore"

  const flipDurationMs = 150
  const EVENT_TYPE_KEY = "##eventHandlerType"
  const actionTypes = getAvailableActions()

  export let key
  export let actions
  export let bindings = []
  export let nested

  let actionQuery
  let selectedAction = actions?.length ? actions[0] : null

  $: {
    // Ensure parameters object is never null
    if (selectedAction && !selectedAction.parameters) {
      selectedAction.parameters = {}
    }
  }
  $: parsedQuery =
    typeof actionQuery === "string" ? actionQuery.toLowerCase().trim() : ""
  $: showAvailableActions = !actions?.length
  $: mappedActionTypes = actionTypes.reduce((acc, action) => {
    let parsedName = action.name.toLowerCase().trim()
    if (parsedQuery.length && parsedName.indexOf(parsedQuery) < 0) {
      return acc
    }
    acc[action.type] = acc[action.type] || []
    acc[action.type].push(action)
    return acc
  }, {})
  // These are ephemeral bindings which only exist while executing actions
  $: eventContexBindings = getEventContextBindings(
    $currentAsset,
    $store.selectedComponentId,
    key,
    actions,
    selectedAction?.id
  )
  $: allBindings = eventContexBindings.concat(bindings)
  $: {
    // Ensure each action has a unique ID
    if (actions) {
      actions.forEach(action => {
        if (!action.id) {
          action.id = generate()
        }
      })
    }
  }
  $: selectedActionComponent =
    selectedAction &&
    actionTypes.find(t => t.name === selectedAction[EVENT_TYPE_KEY])?.component
  $: {
    // Select the first action if we delete an action
    if (selectedAction && !actions?.includes(selectedAction)) {
      selectedAction = actions?.[0]
    }
  }

  const deleteAction = index => {
    actions.splice(index, 1)
    actions = actions
  }

  const toggleActionList = () => {
    actionQuery = null
    showAvailableActions = !showAvailableActions
  }

  const addAction = actionType => {
    const newAction = {
      parameters: {},
      [EVENT_TYPE_KEY]: actionType.name,
      id: generate(),
    }
    if (!actions) {
      actions = []
    }
    actions = [...actions, newAction]
    selectedAction = newAction
  }

  const selectAction = action => () => {
    selectedAction = action
  }

  const onAddAction = actionType => {
    addAction(actionType)
    toggleActionList()
  }

  function handleDndConsider(e) {
    actions = e.detail.items
  }
  function handleDndFinalize(e) {
    actions = e.detail.items
  }
</script>

<DrawerContent>
  <Layout noPadding gap="S" slot="sidebar">
    {#if showAvailableActions || !actions?.length}
      <div class="actions-list">
        {#if actions?.length > 0}
          <div>
            <ActionButton
              secondary
              icon={"ArrowLeft"}
              on:click={toggleActionList}
            >
              Back
            </ActionButton>
          </div>
        {/if}
        <div class="search-wrap">
          <Search placeholder="Search" bind:value={actionQuery} />
        </div>
        {#each Object.entries(mappedActionTypes) as [categoryId, category], idx}
          <div class="heading" class:top-entry={idx === 0}>{categoryId}</div>
          <ul>
            {#each category as actionType}
              <li on:click={onAddAction(actionType)}>
                <span class="action-name">{actionType.name}</span>
              </li>
            {/each}
          </ul>
        {/each}
      </div>
    {/if}

    {#if actions && actions.length > 0 && !showAvailableActions}
      <div>
        <Button secondary on:click={toggleActionList}>Add Action</Button>
      </div>
      <div
        class="actions"
        use:dndzone={{
          items: actions,
          flipDurationMs,
          dropTargetStyle: { outline: "none" },
        }}
        on:consider={handleDndConsider}
        on:finalize={handleDndFinalize}
      >
        {#each actions as action, index (action.id)}
          <div
            class="action-container"
            animate:flip={{ duration: flipDurationMs }}
            class:selected={action === selectedAction}
            on:click={selectAction(action)}
          >
            <Icon name="DragHandle" size="XL" />
            <div class="action-header">
              {index + 1}.&nbsp;{action[EVENT_TYPE_KEY]}
            </div>
            <Icon
              name="Close"
              hoverable
              size="S"
              on:click={() => deleteAction(index)}
            />
          </div>
        {/each}
      </div>
    {/if}
  </Layout>
  <Layout noPadding>
    {#if selectedActionComponent && !showAvailableActions}
      {#key selectedAction.id}
        <div class="selected-action-container">
          <svelte:component
            this={selectedActionComponent}
            parameters={selectedAction.parameters}
            bindings={allBindings}
            {nested}
          />
        </div>
      {/key}
    {/if}
  </Layout>
</DrawerContent>

<style>
  .actions {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    gap: var(--spacing-s);
  }
  .action-header {
    color: var(--spectrum-global-color-gray-700);
    flex: 1 1 auto;
  }
  .action-container {
    background-color: var(--background);
    padding: var(--spacing-s) var(--spacing-m);
    border-radius: 4px;
    border: var(--border-light);
    transition: background-color 130ms ease-in-out, color 130ms ease-in-out,
      border-color 130ms ease-in-out;
    gap: var(--spacing-m);
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
  }
  .action-container:hover,
  .action-container.selected {
    background-color: var(--spectrum-global-color-gray-50);
    border-color: var(--spectrum-global-color-gray-500);
    cursor: pointer;
  }
  .action-container:hover .action-header,
  .action-container.selected .action-header {
    color: var(--spectrum-global-color-gray-900);
  }

  .actions-list > * {
    padding-bottom: var(--spectrum-global-dimension-static-size-200);
  }

  .actions-list .heading {
    padding-bottom: var(--spectrum-global-dimension-static-size-100);
    padding-top: var(--spectrum-global-dimension-static-size-50);
  }

  .actions-list .heading.top-entry {
    padding-top: 0px;
  }

  ul {
    list-style: none;
    padding: 0;
    margin: 0;
  }

  li {
    font-size: var(--font-size-s);
    padding: var(--spacing-m);
    border-radius: 4px;
    background-color: var(--spectrum-global-color-gray-200);
    transition: background-color 130ms ease-in-out, color 130ms ease-in-out,
      border-color 130ms ease-in-out;
    word-wrap: break-word;
  }
  li:not(:last-of-type) {
    margin-bottom: var(--spacing-s);
  }
  li :global(*) {
    transition: color 130ms ease-in-out;
  }
  li:hover {
    color: var(--spectrum-global-color-gray-900);
    background-color: var(--spectrum-global-color-gray-50);
    cursor: pointer;
  }

  .action-name {
    font-weight: 600;
    text-transform: capitalize;
  }
  .heading {
    font-size: var(--font-size-s);
    font-weight: 600;
    text-transform: uppercase;
    color: var(--spectrum-global-color-gray-600);
  }
</style>
