<script setup>

import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;

import { ref, onMounted, computed, useTemplateRef } from 'vue';
import { Button, ButtonGroup, TextInput, Dropdown, TableView, InputDialog } from 'pankow';
import { ROLES } from '../constants.js';
import Section from '../components/Section.vue';
import UserDialog from '../components/UserDialog.vue';
import GroupDialog from '../components/GroupDialog.vue';
import ImpersonateDialog from '../components/ImpersonateDialog.vue';
import PasswordResetDialog from '../components/PasswordResetDialog.vue';
import UsersModel from '../models/UsersModel.js';
import GroupsModel from '../models/GroupsModel.js';
import ProfileModel from '../models/ProfileModel.js';

const usersModel = UsersModel.create();
const groupsModel = GroupsModel.create();
const profileModel = ProfileModel.create();

const usersColumns = {
  role: { width: '33.5px' },
  user: { label: t('users.users.user'), sort: true },
  groups: { label: t('users.users.groups'), sort: true },
  actions: {}
};

const groupsColumns = {
  name: { label: t('users.groups.name'), sort: true },
  users: { label: t('users.groups.users'), sort: true },
  actions: {}
};

// TODO maybe replace with app.provide and inject
const profile = ref({});
const busy = ref(true);
const filterOptions = ref([
  { id: 'all', name: 'All Users' },
  { id: 'active', name: 'Active Users' },
  { id: 'inactive', name: 'Inactive Users' }
]);
const users = ref([]);
const usersById = ref({});
const search = ref('');
const filter = ref(filterOptions.value[0].id);
const groups = ref([]);
const groupsById = ref({});
const roles = ref([]);

const inputDialog = useTemplateRef('inputDialog');
const userDialog = useTemplateRef('userDialog');
const groupDialog = useTemplateRef('groupDialog');
const impersonateDialog = useTemplateRef('impersonateDialog');
const passwordResetDialog = useTemplateRef('passwordResetDialog');

const filteredUsers = computed(() => {
  return users.value.filter(u => {
    return u.username.indexOf(search.value) !== -1 || u.email.indexOf(search.value) !== -1 || u.displayName.indexOf(search.value) !== -1;
  }).filter(u => {
    if (filter.value === 'active') {
      return u.active;
    } else if (filter.value === 'inactive') {
      return !u.active;
    } else {
      return true;
    }
  });
});

async function refreshUsers() {
  const [error, result] = await usersModel.list();
  if (error) return console.error(error);

  usersById.value = {};
  users.value = result;
  result.forEach(user => {
    usersById.value[user.id] = user;
  });
}

async function refreshGroups() {
  const [error, result] = await groupsModel.list();
  if (error) return console.error(error);

  groupsById.value = {};
  groups.value = result;
  result.forEach(group => {
    groupsById.value[group.id] = group;
  });
}

function canEdit(user) {
  const roleInt1 = roles.value.findIndex(function (role) { return role.id === profile.value.role; });
  const roleInt2 = roles.value.findIndex(function (role) { return role.id === user.role; });

  return (roleInt1 - roleInt2) >= 0;
}

function canImpersonate(user) {
  // only admins can impersonate
  if (!profile.value.isAtLeastAdmin) return false;

  // only users with username can be impersonated
  if (!user.username) return false;

  // normal admins cannot impersonate owners
  if (!profile.value.isAtLeastOwner && [ ROLES.OWNER ].indexOf(user.role) !== -1) return false;

  return true;
}

function isMe(user) {
  return user.username === profile.value.username;
}

function groupMembers(group) {
  return group.userIds.filter(function (uid) { return !!usersById.value[uid]; }).map(function (uid) { return usersById.value[uid].username || usersById.value[uid].email; }).join(' ');
}

function onImpersonate(user) {
  impersonateDialog.value.open(user);
}

function onPasswordReset(user) {
  passwordResetDialog.value.open(user);
}

function onEditOrAddUser(user = null) {
  userDialog.value.open(user);
}

function onInvitation(user) {
  // TODO
}

function onEditOrAddGroup(group = null) {
  groupDialog.value.open(group);
}

async function onRemoveGroup(group) {
  const yes = await inputDialog.value.confirm({
    title: t('users.deleteGroupDialog.title', { name: group.name }),
    message: t('users.deleteGroupDialog.description', { memberCount: group.memberCount }),
    confirmStyle: 'danger',
    confirmLabel: t('users.deleteGroupDialog.deleteAction'),
    rejectLabel: t('main.dialog.cancel')
  });

  if (!yes) return;

  const [error] = await groupsModel.remove(group.id);
  if (error) console.error(error);

  await refreshGroups();
}

async function onRemoveUser(user) {
  const yes = await inputDialog.value.confirm({
    title: t('users.deleteUserDialog.title', { username: (user.username || user.email) }),
    message: t('users.deleteUserDialog.description'),
    confirmStyle: 'danger',
    confirmLabel: t('users.deleteUserDialog.deleteAction'),
    rejectLabel: t('main.dialog.cancel')
  });

  if (!yes) return;

  const [error] = await usersModel.remove(user.id);
  if (error) console.error(error);

  await refreshUsers();
}

onMounted(async () => {
  const [error, result] = await profileModel.get();
  if (error) return console.error(error);

  profile.value = result;

  await refreshUsers();
  await refreshGroups();

  // Order matters for permissions used in canEdit
  roles.value = [
    { id: 'user', name: t('users.role.user'), disabled: false },
    { id: 'usermanager', name: t('users.role.usermanager'), disabled: false },
    { id: 'mailmanager', name: t('users.role.mailmanager'), disabled: false },
    { id: 'admin', name: t('users.role.admin'), disabled: !profile.value.isAtLeastAdmin },
    { id: 'owner', name: t('users.role.owner'), disabled: !profile.value.isAtLeastOwner }
  ];

  busy.value = false;
});

</script>

<template>
  <InputDialog ref="inputDialog" />
  <UserDialog ref="userDialog" @success="refreshUsers()"/>
  <GroupDialog ref="groupDialog" @success="refreshGroups()"/>
  <ImpersonateDialog ref="impersonateDialog" />
  <PasswordResetDialog ref="passwordResetDialog" />

  <div class="content">
    <Section :title="$t('main.navbar.users')">
      <template #header-buttons>
        <TextInput v-model="search" placeholder="Search ..." />
        <Dropdown outline tool :options="filterOptions" option-key="id" option-label="name" v-model="filter"></Dropdown>
        <Button icon="fa-solid fa-user-plus" @click="onEditOrAddUser()">{{ $t('users.newUserAction') }}</Button>
      </template>

      <TableView :columns="usersColumns" :model="filteredUsers" :busy="busy" style="max-height: 400px;" @row-click="onEditOrAddUser">
        <template #role="user">
          <i class="fas fa-crown arrow" v-if="user.active && user.role === 'owner'" v-tooltip="$t('users.users.superadminTooltip')"></i>
          <i class="fa fa-user-tie arrow" v-if="user.active && user.role === 'admin'" v-tooltip="$t('users.users.adminTooltip')"></i>
          <i class="fas fa-users-cog arrow" v-if="user.active && user.role === 'usermanager'" v-tooltip="$t('users.users.usermanagerTooltip')"></i>
          <i class="fas fa-mail-bulk arrow" v-if="user.active && user.role === 'mailmanager'" v-tooltip="$t('users.users.mailmanagerTooltip')"></i>
          <i class="fa fa-ban" v-if="!user.active" v-tooltip="$t('users.users.inactiveTooltip')"></i>
        </template>
        <template #user="user">
          {{ user.displayName }} &nbsp; <span class="text-muted">{{ user.username }}</span> &nbsp; <i v-show="user.source" class="far fa-address-book" v-tooltip="$t('users.users.externalLdapTooltip')"></i>
        </template>
        <template #groups="user">
          <span class="group-label" v-for="groupId in user.groupIds" :key="groupId">
            {{ groupsById[groupId] ? groupsById[groupId].name : groupId }}
          </span>
        </template>
        <template #actions="user">
          <div class="table-actions">
            <ButtonGroup>
              <Button small tool secondary :disabled="!canEdit(user)" v-if="!user.inviteAccepted && !isMe(user) && !user.source" @click.stop="onInvitation(user)" v-tooltip="$t('users.users.invitationTooltip')" icon="fa-solid fa-paper-plane" />
              <Button small tool secondary :disabled="!canEdit(user)" v-if="user.inviteAccepted && !user.source" @click.stop="onPasswordReset(user)" v-tooltip="$t('users.users.resetPasswordTooltip')" icon="fa-solid fa-key" />
              <Button small tool secondary :disabled="!canImpersonate(user)" @click.stop="onImpersonate(user)" v-tooltip="$t('users.users.setGhostTooltip')" icon="fa-solid fa-user-secret" />
              <Button small tool secondary :disabled="!canEdit(user)" @click.stop="onEditOrAddUser(user)" v-tooltip="$t('users.users.editUserTooltip')" icon="fa fa-pencil-alt" />
            </ButtonGroup>
            <Button small tool danger :disabled="!canEdit(user) || isMe(user)" @click.stop="onRemoveUser(user)" v-tooltip="$t('users.users.removeUserTooltip')" icon="far fa-trash-alt" />
          </div>
        </template>
      </TableView>
      <br/>
      <div>{{ $t('users.users.count', { count: users.length }) }}</div>
    </Section>

    <Section :title="$t('users.groups.title')">
      <template #header-buttons>
        <Button icon="fa-solid fa-plus" @click="onEditOrAddGroup()">{{ $t('users.groups.newGroupAction') }}</Button>
      </template>

      <TableView :columns="groupsColumns" :model="groups" :busy="busy" style="max-height: 400px;" @row-click="onEditOrAddGroup">
        <template #name="group">
          {{ group.name }} &nbsp; <i v-if="group.source" class="far fa-address-book" v-tooltip="$t('users.groups.externalLdapTooltip')"></i>
        </template>
        <template #users="group">
          {{ groupMembers(group) }}
        </template>
        <template #actions="group">
          <div class="table-actions">
            <ButtonGroup>
              <Button tool small secondary @click.stop="onEditOrAddGroup(group)" v-tooltip="'Edit Group'" icon="fa fa-pencil-alt" />
            </ButtonGroup>
            <Button tool small danger @click.stop="onRemoveGroup(group)" v-tooltip="'Remove Group'" icon="far fa-trash-alt" />
          </div>
        </template>
      </TableView>
    </Section>
  </div>
</template>

<style scoped>

.group-label {
  padding: 0 5px;
}

</style>
