<template>
  <Context
    class="select user-source-users-context"
    :class="{ 'context--loading-overlay': state === 'loading' }"
    max-height-if-outside-viewport
    @shown="shown"
  >
    <SelectSearch
      v-model="search"
      :placeholder="$t('userSourceUsersContext.searchPlaceholder')"
    />
    <div v-auto-overflow-scroll class="user-source-users-context__user-list">
      <ul class="select__items select__items--no-max-height">
        <li
          class="select__item"
          :class="{
            'select__item--selected': !isAuthenticated,
            'select__item--loading':
              currentUser === null && state === 'loggingIn',
          }"
        >
          <a class="select__item-link">
            <Presentation
              :title="$t('userSourceUsersContext.anonymous')"
              size="medium"
              :initials="
                $t('userSourceUsersContext.anonymous') | nameAbbreviation
              "
              @click="selectUser()"
            />
          </a>
        </li>
        <template v-for="userSource in userSources">
          <template v-if="usersPerUserSources[userSource.id]?.length > 0">
            <li
              :key="`user_source_${userSource.id}`"
              class="select__item-label"
            >
              {{ userSource.name }}
            </li>
            <li
              v-for="user in usersPerUserSources[userSource.id]"
              :key="`user_source_${userSource.id}__${user.id}`"
              class="select__item"
              :class="{
                'select__item--selected': isSelectedUser(userSource, user),
                'select__item--loading':
                  isSelectedUser(userSource, user) && state === 'loggingIn',
              }"
            >
              <a class="select__item-link">
                <Presentation
                  :title="getUserTitle(user, userSource.name)"
                  :subtitle="user.email || $t('userSourceUsersContext.noEmail')"
                  size="medium"
                  :initials="
                    user.username ||
                    $t('userSourceUsersContext.unnamed') | nameAbbreviation
                  "
                  @click="selectUser(user)"
                />
              </a>
            </li>
          </template>
        </template>
      </ul>
    </div>
  </Context>
</template>

<script>
import context from '@baserow/modules/core/mixins/context'
import { mapActions } from 'vuex'
import UserSourceService from '@baserow/modules/core/services/userSource'
import { notifyIf } from '@baserow/modules/core/utils/error'
import _ from 'lodash'
import { DEFAULT_USER_ROLE_PREFIX } from '@baserow/modules/builder/constants'

export default {
  name: 'UserSourceUsersContext',
  mixins: [context],
  inject: ['page', 'builder'],
  data() {
    return {
      state: null,
      search: '',
      currentUser: null,
      usersPerUserSources: {},
    }
  },
  computed: {
    isAuthenticated() {
      return this.$store.getters['userSourceUser/isAuthenticated'](this.builder)
    },
    loggedUser() {
      return this.$store.getters['userSourceUser/getUser'](this.builder)
    },
    userSources() {
      return this.$store.getters['userSource/getUserSources'](this.builder)
    },
  },
  watch: {
    search(newVal) {
      this.debouncedSearchUpdate(this, newVal)
    },
    /**
     * If the userSource changes, we want to ensure the User Roles in the
     * selector are still valid.
     */
    userSources: {
      handler() {
        this.refreshUserRoles()
      },
      deep: true,
    },
  },
  methods: {
    ...mapActions({
      actionForceAuthenticate: 'userSourceUser/forceAuthenticate',
      actionLogoff: 'userSourceUser/logoff',
    }),

    /**
     * When called, the authentication is forced which causes the
     * token to be refreshed. Since the user's role is in the token,
     * re-authenticating will cause the user's role to be also updated
     * in the context.
     */
    async refreshUserRoles() {
      if (!this.currentUser) {
        await this.actionLogoff({ application: this.builder })
      } else {
        const userSource = this.$store.getters['userSource/getUserSourceByUId'](
          this.builder,
          this.currentUser.user_source_uid
        )
        if (userSource) {
          await this.actionForceAuthenticate({
            application: this.builder,
            userSource,
            user: this.currentUser,
          })
        }
      }
    },

    getUserTitle(user, userSourceName) {
      const username =
        user.username || this.$t('userSourceUsersContext.unnamed')
      if (user.role.startsWith(DEFAULT_USER_ROLE_PREFIX)) {
        return this.$t('userSelector.member', {
          prefix: `${username} - ${userSourceName}`,
        })
      } else if (!user.role.trim().length) {
        return `${username} - ${this.$t('visibilityForm.noRole')}`
      } else {
        return `${username} - ${user.role}`
      }
    },

    async shown() {
      if (this.userSources.length === 0) {
        this.state = 'loaded'
        return
      }
      if (this.state !== 'loaded') {
        this.state = 'loading'
      }
      try {
        this.currentUser = this.loggedUser
        const {
          data: { users_per_user_sources: userSourceUsers },
        } = await UserSourceService(this.$client).getUserSourceUsers(
          this.builder.id
        )
        this.usersPerUserSources = userSourceUsers
      } catch (error) {
        this.state = 'error'
        notifyIf(error)
      }
      this.state = 'loaded'
    },

    isSelectedUser(userSource, user) {
      return (
        this.currentUser?.user_source_uid === userSource.uid &&
        this.currentUser?.id === user.id
      )
    },

    async selectUser(user) {
      this.state = 'loggingIn'
      const previousUser = this.currentUser
      this.currentUser = user
      try {
        if (!user) {
          await this.actionLogoff({ application: this.builder })
        } else {
          const userSource = this.$store.getters[
            'userSource/getUserSourceById'
          ](this.builder, user.user_source_id)
          await this.actionForceAuthenticate({
            application: this.builder,
            userSource,
            user,
          })
        }
      } catch {
        this.currentUser = previousUser
      } finally {
        this.state = 'loaded'
      }
      this.hide()
    },

    debouncedSearchUpdate: _.debounce(async (self, search) => {
      const {
        data: { users_per_user_sources: userSourceUsers },
      } = await UserSourceService(self.$client).getUserSourceUsers(
        self.builder.id,
        search
      )
      self.usersPerUserSources = userSourceUsers
    }, 300),
  },
}
</script>