<script>
  import { getContext } from "svelte"
  import { Table } from "@budibase/bbui"
  import SlotRenderer from "./SlotRenderer.svelte"
  import { onDestroy } from "svelte"
  import { canBeSortColumn } from "@budibase/shared-core"

  export let dataProvider
  export let columns
  export let rowCount
  export let quiet
  export let size
  export let allowSelectRows
  export let compact
  export let onClick
  export let noRowsMessage

  const component = getContext("component")
  const { styleable, getAction, ActionTypes, rowSelectionStore } =
    getContext("sdk")
  const customColumnKey = `custom-${Math.random()}`
  const customRenderers = [
    {
      column: customColumnKey,
      component: SlotRenderer,
    },
  ]

  let selectedRows = []

  $: hasChildren = $component.children
  $: loading = dataProvider?.loading ?? false
  $: data = dataProvider?.rows || []
  $: fullSchema = dataProvider?.schema ?? {}
  $: primaryDisplay = dataProvider?.primaryDisplay
  $: fields = getFields(fullSchema, columns, false, primaryDisplay)
  $: schema = getFilteredSchema(fullSchema, fields, hasChildren)
  $: setSorting = getAction(
    dataProvider?.id,
    ActionTypes.SetDataProviderSorting
  )
  $: table = dataProvider?.datasource?.type === "table"
  $: {
    rowSelectionStore.actions.updateSelection(
      $component.id,
      selectedRows.length ? selectedRows[0].tableId : "",
      selectedRows.map(row => row._id)
    )
  }

  // If the data changes, double check that the selected elements are still present.
  $: if (data) {
    let rowIds = data.map(row => row._id)
    if (rowIds.length) {
      selectedRows = selectedRows.filter(row => rowIds.includes(row._id))
    }
  }

  const getFields = (
    schema,
    customColumns,
    showAutoColumns,
    primaryDisplay
  ) => {
    if (customColumns?.length) {
      return customColumns
    }

    // Otherwise generate columns
    let columns = []
    let autoColumns = []
    Object.entries(schema).forEach(([field, fieldSchema]) => {
      if (fieldSchema.visible === false) {
        return
      }
      if (!fieldSchema?.autocolumn) {
        columns.push(field)
      } else if (showAutoColumns) {
        autoColumns.push(field)
      }
    })

    // Sort columns to respect grid metadata
    const allCols = columns.concat(autoColumns)
    return allCols.sort((a, b) => {
      if (a === primaryDisplay) {
        return -1
      }
      if (b === primaryDisplay) {
        return 1
      }
      const aOrder = schema[a].order
      const bOrder = schema[b].order
      if (aOrder === bOrder) {
        return 0
      }
      if (aOrder == null) {
        return 1
      }
      if (bOrder == null) {
        return -1
      }
      return aOrder < bOrder ? -1 : 1
    })
  }

  const getFilteredSchema = (schema, fields, hasChildren) => {
    let newSchema = {}
    if (hasChildren) {
      newSchema[customColumnKey] = {
        displayName: null,
        order: 0,
        sortable: false,
        divider: true,
        width: "auto",
        preventSelectRow: true,
      }
    }

    fields.forEach(field => {
      const columnName = typeof field === "string" ? field : field.name
      if (!schema[columnName]) {
        return
      }
      newSchema[columnName] = schema[columnName]
      if (!canBeSortColumn(schema[columnName].type)) {
        newSchema[columnName].sortable = false
      }

      // Add additional settings like width etc
      if (typeof field === "object") {
        newSchema[columnName] = {
          ...newSchema[columnName],
          ...field,
        }
      }
    })
    return newSchema
  }

  const onSort = e => {
    setSorting({
      column: e.detail.column,
      order: e.detail.order,
    })
  }

  const handleClick = e => {
    if (onClick) {
      onClick({ row: e.detail })
    }
  }

  onDestroy(() => {
    rowSelectionStore.actions.updateSelection($component.id, [])
  })
</script>

<div use:styleable={$component.styles} class={size}>
  <Table
    {data}
    {schema}
    {loading}
    {rowCount}
    {quiet}
    {compact}
    {customRenderers}
    allowSelectRows={allowSelectRows && table}
    bind:selectedRows
    allowEditRows={false}
    allowEditColumns={false}
    showAutoColumns={true}
    disableSorting
    autoSortColumns={!columns?.length}
    on:sort={onSort}
    on:click={handleClick}
    placeholderText={noRowsMessage || "No rows found"}
  >
    <slot />
  </Table>
  {#if allowSelectRows && selectedRows.length}
    <div class="row-count">
      {selectedRows.length} row{selectedRows.length === 1 ? "" : "s"} selected
    </div>
  {/if}
</div>

<style>
  div {
    background-color: var(--spectrum-alias-background-color-secondary);
  }
  .row-count {
    margin-top: var(--spacing-l);
  }
</style>
