<!--
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
  import activity, { TxViewlet, ActivityFilter } from '@hcengineering/activity'
  import chunter from '@hcengineering/chunter'
  import core, { Class, Doc, Ref, SortingOrder } from '@hcengineering/core'
  import { getResource, IntlString } from '@hcengineering/platform'
  import { createQuery, getClient } from '@hcengineering/presentation'
  import notification from '@hcengineering/notification'
  import { Component, Grid, IconActivity, Label, Scroller, Button, showPopup } from '@hcengineering/ui'
  import { ActivityKey, activityKey, DisplayTx, newActivity } from '../activity'
  import TxView from './TxView.svelte'
  import { filterCollectionTxes } from '../utils'
  import { Writable } from 'svelte/store'
  import view from '@hcengineering/view'
  import activityPlg from '../plugin'
  import FilterPopup from './FilterPopup.svelte'

  export let object: Doc
  export let integrate: boolean = false
  export let showCommenInput: boolean = true
  export let transparent: boolean = false

  let txes: DisplayTx[] = []

  const client = getClient()
  const attrs = client.getHierarchy().getAllAttributes(object._class)

  let labels: IntlString[] = []
  const filters: ActivityFilter[] = []
  const saved = localStorage.getItem('activity-filter')
  let selectedFilter: Ref<Doc>[] | 'All' =
    saved !== null && saved !== undefined ? (JSON.parse(saved) as Ref<Doc>[] | 'All') : 'All'
  $: localStorage.setItem('activity-filter', JSON.stringify(selectedFilter))
  client.findAll(activity.class.ActivityFilter, {}).then((res) => {
    res.map((it) => filters.push(it))
    if (saved !== null && saved !== undefined) {
      const temp: Ref<Doc>[] | 'All' = JSON.parse(saved)
      if (temp !== 'All' && Array.isArray(temp)) {
        selectedFilter = temp.filter((it) => filters.findIndex((f) => it === f._id) > -1)
        if ((selectedFilter as Ref<Doc>[]).length === 0) selectedFilter = 'All'
      } else selectedFilter = 'All'
    }
  })

  const activityQuery = newActivity(client, attrs)
  getResource(notification.function.GetNotificationClient).then((res) => {
    lastViews = res().getLastViews()
  })
  let lastViews: Writable<Map<Ref<Doc>, number>> | undefined

  let viewlets: Map<ActivityKey, TxViewlet>

  let allViewlets: TxViewlet[] = []
  let editableMap: Map<Ref<Class<Doc>>, boolean> | undefined = undefined

  const descriptors = createQuery()
  $: descriptors.query(activity.class.TxViewlet, {}, (result) => {
    allViewlets = result
    editableMap = new Map(
      allViewlets
        .filter((tx) => tx.txClass === core.class.TxCreateDoc)
        .map((it) => [it.objectClass, it.editable ?? false])
    )
  })

  $: viewlets = new Map(allViewlets.map((r) => [activityKey(r.objectClass, r.txClass), r]))

  function updateTxes (object: Doc): void {
    activityQuery.update(
      object,
      (result) => {
        txes = filterCollectionTxes(result)
      },
      SortingOrder.Descending,
      editableMap ?? new Map()
    )
  }

  $: if (editableMap) updateTxes(object)

  $: newTxPos = newTx(filtered, $lastViews)

  function newTx (txes: DisplayTx[], lastViews: Map<Ref<Doc>, number> | undefined): number {
    const lastView = lastViews?.get(object._id)
    if (lastView === undefined || lastView === -1) return -1
    for (let index = 0; index < txes.length; index++) {
      const tx = txes[index]
      if (tx.tx.modifiedOn <= lastView) return index - 1
    }
    return -1
  }

  let optionsBtn: HTMLButtonElement
  const handleOptions = () => {
    showPopup(
      FilterPopup,
      { selectedFilter, filters },
      optionsBtn,
      () => {},
      (res) => {
        if (res === undefined) return
        if (res.action === 'select') selectedFilter = res.value as Ref<Doc>[] | 'All'
      }
    )
  }

  let filterActions: ((tx: DisplayTx, _class?: Ref<Doc>) => boolean)[] = [] // Enabled filters
  const updateFiltered = () => (filtered = txes.filter((it) => filterActions.some((f) => f(it, object._class))))
  async function updateFilterActions (fls: ActivityFilter[], selected: Ref<Doc>[] | 'All'): Promise<void> {
    if (selected === 'All' || !Array.isArray(selected)) filterActions = [() => true]
    else {
      const tf = fls.filter((filter) => (selected as Ref<Doc>[]).includes(filter._id))
      filterActions = []
      labels = []
      tf.forEach((filter) => {
        labels.push(filter.label)
        getResource(filter.filter).then((res) => filterActions.push(res))
      })
    }
    setTimeout(() => updateFiltered(), 0)
  }
  $: updateFilterActions(filters, selectedFilter)
  $: filtered = txes.filter((it) => filterActions.some((f) => f(it, object._class)))
</script>

{#if !integrate || transparent}
  {#if transparent !== undefined && !transparent}
    <div class="ac-header short mirror-tool highlight">
      <div class="ac-header__wrap-title">
        <div class="flex-center icon"><IconActivity size={'small'} /></div>
        <span class="ac-header__title"><Label label={activity.string.Activity} /></span>
      </div>
    </div>
  {/if}
  <div class="flex-col flex-grow min-h-0" class:background-accent-bg-color={!transparent}>
    <Scroller>
      <div class="p-10 select-text" id={activity.string.Activity}>
        {#if filtered}
          <Grid column={1} rowGap={1.5}>
            {#each filtered as tx, i}
              <TxView
                {tx}
                {viewlets}
                isNew={newTxPos >= i && newTxPos !== -1}
                isNextNew={newTxPos > i && newTxPos !== -1}
              />
            {/each}
          </Grid>
        {/if}
      </div>
    </Scroller>
    {#if showCommenInput}
      <div class="ref-input">
        <Component is={chunter.component.CommentInput} props={{ object, shouldSaveDraft: true }} />
      </div>
    {/if}
  </div>
{:else}
  <slot />
  <!-- <div class="antiDivider" style:margin={'1rem -1.5rem'} /> -->
  <div class="antiSection-header mt-6">
    <div class="antiSection-header__icon"><IconActivity size={'small'} /></div>
    <span class="antiSection-header__title"><Label label={activity.string.Activity} /></span>
    {#if selectedFilter === 'All'}
      <span class="antiSection-header__tag highlight"><Label label={activityPlg.string.All} /></span>
    {:else}
      {#each labels as label}
        <span class="antiSection-header__tag overflow-label"><Label {label} /></span>
      {/each}
    {/if}
    <div class="w-2 min-w-2 max-w-2" />
    <Button
      bind:input={optionsBtn}
      icon={view.icon.ViewButton}
      kind={'transparent'}
      shape={'circle'}
      on:click={handleOptions}
    />
  </div>
  {#if showCommenInput}
    <div class="ref-input">
      <Component is={chunter.component.CommentInput} props={{ object, shouldSaveDraft: true }} />
    </div>
  {/if}
  <div class="p-activity select-text" id={activity.string.Activity}>
    {#if filtered}
      <Grid column={1} rowGap={1.5}>
        {#each filtered as tx, i}
          <TxView
            {tx}
            {viewlets}
            isNew={newTxPos >= i && newTxPos !== -1}
            isNextNew={newTxPos > i && newTxPos !== -1}
          />
        {/each}
      </Grid>
    {/if}
  </div>
{/if}

<style lang="scss">
  .icon {
    margin-left: 1rem;
    height: 2rem;
    color: var(--caption-color);
  }
  .ref-input {
    flex-shrink: 0;
    padding: 0.75rem 0;
  }
  .p-activity {
    padding: 1.5rem 0;
  }

  :global(.grid .msgactivity-container:last-child::after) {
    content: none;
  } // Remove the line in the last Activity message
</style>
