import './ownership-transfer-popup'
import './role-selector'

import gql from 'graphql-tag'
import { css, html, LitElement } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import { connect } from 'pwa-helpers/connect-mixin'

import { client, gqlContext } from '@operato/graphql'
import { i18next } from '@operato/i18n'
import { openPopup } from '@operato/layout'
import { store } from '@operato/shell'
import { OxPrompt } from '@operato/popup/ox-prompt.js'

import { RoleSelector } from './role-selector'
import { InheritedValueType } from '@operato/data-grist'

@customElement('user-role-editor')
class UserRoleEditor extends connect(store)(LitElement) {
  static styles = [
    css`
      :host {
        display: flex;
        flex-direction: column;

        border: 1px solid var(--primary-color);
        font: normal 15px var(--theme-font);
        color: var(--secondary-color);
      }
      ul {
        flex: 1;
        display: grid;
        grid-template-columns: 1fr 1fr;
        column-gap: 20px;

        overflow: auto;
        margin: 0;
        padding: var(--padding-default);
        list-style: none;
        border: 1px dashed rgba(0, 0, 0, 0.1);
        border-width: 1px 0;
      }
      input[readonly] {
        border: none;
        background-color: transparent;
        color: var(--secondary-color);
        font: var(--input-font);
      }
      li {
        padding: var(--padding-narrow);
      }
      [detail] {
        background-color: rgba(var(--primary-color-rgb), 0.05);
      }
      [detail] mwc-icon {
        vertical-align: middle;
        --mdc-icon-size: var(--fontsize-default);
        color: var(--primary-color);
      }
      [detail] li {
        display: flex;
      }
      [detail] li input {
        flex: 1;
      }

      [buttons] {
        margin: 0;
        padding: 5px;
        background-color: rgba(var(--primary-color-rgb), 0.2);
      }

      mwc-button {
        margin: 5px;
      }

      [danger] {
        --mdc-theme-primary: var(--mdc-danger-button-primary-color);
      }
      [outlined] {
        background-color: var(--theme-white-color);
      }

      @media screen and (max-width: 480px) {
        ul {
          grid-template-columns: 1fr;
        }
      }
    `
  ]

  @property({ type: Object }) user: any = {}
  @property({ type: Boolean }) activate: boolean = false
  @property({ type: Object }) domainOwner: any

  @state() domainRoles: any[] = []
  @state() grantedRoles: any = {}
  @state() userRoles: any[] = []
  @state() isSuperOwner: boolean = false
  @state() isDomainOwner: boolean = false

  private me: any

  render() {
    const user = this.user || {}
    const domainRoles = this.domainRoles || []
    const grantedRoles = this.grantedRoles || {}
    let currentDomainName = 'roles'
    if (domainRoles?.length) {
      currentDomainName = domainRoles[0].domain.name
    }

    return html`
      ${this.activate
        ? html`
            <ul detail>
              ${user.userType === 'user'
                ? html`
                    <li>
                      <span>
                        <mwc-icon>email</mwc-icon>
                        ${user.email}
                      </span>
                    </li>
                  `
                : ''}
            </ul>

            <role-selector .user=${this.user} .roles=${domainRoles} .userRoles=${this.userRoles}></role-selector>

            ${Object.keys(grantedRoles).map(
              partnerName => html`
                <role-selector
                  .roleCategory=${partnerName}
                  .user=${this.user}
                  .roles=${this.grantedRoles[partnerName]}
                  .userRoles=${this.userRoles}
                >
                </role-selector>
              `
            )}
          `
        : ''}

      <div buttons>
        ${this.isRoleEditable()
          ? html` <mwc-button
              raised
              @click=${() => {
                const { availableRoles, selectedRoles } = Array.from(
                  this.renderRoot.querySelectorAll('role-selector') as NodeListOf<RoleSelector>
                ).reduce(
                  (roles, roleSelector) => {
                    roles.availableRoles = roles.availableRoles.concat(
                      roleSelector.roles.map(r => {
                        delete r.domain
                        return r
                      })
                    )
                    roles.selectedRoles = roles.selectedRoles.concat(
                      roleSelector.selectedRoles().map(r => {
                        //@ts-ignore
                        delete r.domain
                        return r
                      })
                    )
                    return roles
                  },
                  { availableRoles: [], selectedRoles: [] } as { availableRoles: any[]; selectedRoles: any[] }
                )
                this.onSave(user, availableRoles, selectedRoles)
              }}
              label=${String(i18next.t('button.save'))}
            ></mwc-button>`
          : ''}
        ${user.userType === 'user'
          ? html`
              ${this.isTransferable(user) && this.userRoles && this.userRoles.length > 0
                ? html`
                    <mwc-button
                      raised
                      danger
                      @click=${() => this.onTransfer(user)}
                      label=${String(i18next.t('button.transfer owner'))}
                    ></mwc-button>
                  `
                : ''}
              ${this.isPasswordResettable(user)
                ? html`<mwc-button
                    raised
                    danger
                    @click="${() => this.onResetPassword(user)}"
                    label=${String(i18next.t('title.reset password'))}
                  ></mwc-button>`
                : ''}
            `
          : ''}
        ${this.isRemovable(user)
          ? html`
              <mwc-button
                raised
                danger
                label=${String(i18next.t('button.delete user'))}
                @click=${() => this.onDelete(user)}
              ></mwc-button>
            `
          : ''}
        ${this.isActivatable(user)
          ? html`<mwc-button raised danger @click="${() => this.onActivate(user)}" label="activate"></mwc-button>`
          : ''}
        ${this.isInactivatable(user)
          ? html`<mwc-button raised danger @click="${() => this.onInactivate(user)}" label="inactivate"></mwc-button>`
          : ''}
      </div>
    `
  }

  isRemovable(user) {
    return this.isDomainOwner && user.email !== this.me.email && !user.owner
  }

  isTransferable(user) {
    return this.activate && (this.isSuperOwner || this.isDomainOwner) && !user.owner
  }

  isRoleEditable() {
    return this.activate && (this.domainRoles?.length || Object.keys(this.grantedRoles || {}).length)
  }

  isPasswordResettable(user) {
    return (this.isSuperOwner || this.isDomainOwner) && user.email !== this.me.email
  }

  isActivatable(user) {
    return (this.isSuperOwner || this.isDomainOwner) && !this.activate
  }

  isInactivatable(user) {
    return (
      (this.isSuperOwner || this.isDomainOwner) &&
      user.id !== this.domainOwner.id &&
      user.userType !== 'admin' &&
      this.activate
    )
  }

  async updated(changedProps) {
    if (changedProps.has('user')) {
      this.fetchAvailableRoles()

      if (this.user?.email) {
        this.userRoles = await this.fetchRolesOnUser(this.user.email)
      } else {
        this.userRoles = []
      }
    }
  }

  async fetchRolesOnUser(email) {
    const response = await client.query({
      query: gql`
        query ($email: EmailAddress!) {
          user(email: $email) {
            id
            name
            email
            roles {
              id
              name
              description
              domain {
                id
              }
            }
          }
        }
      `,
      variables: { email },
      context: gqlContext()
    })

    if (!response.errors) {
      return response.data.user?.roles || []
    }
  }

  async fetchAvailableRoles() {
    const response = await client.query({
      query: gql`
        query roles($inherited: InheritedValueType) {
          roles(inherited: $inherited) {
            items {
              id
              domain {
                name
              }
              name
              description
            }
            total
          }
          grantedRoles {
            id
            role {
              id
              name
              description
              domain {
                name
              }
            }
          }
        }
      `,
      variables: {
        inherited: InheritedValueType.Include
      },
      context: gqlContext()
    })

    if (!response.errors?.length) {
      this.domainRoles = response.data.roles.items || []
      this.grantedRoles = response.data.grantedRoles.reduce((grantedRoles, grantedRole) => {
        const { role } = grantedRole
        const { domain } = role
        if (grantedRoles[domain.name]) {
          grantedRoles[domain.name].push(role)
        } else {
          grantedRoles[domain.name] = [role]
        }

        return grantedRoles
      }, {})
    } else {
      this.domainRoles = []
      this.grantedRoles = []
    }
  }

  async onSave(user, availableRoles, selectedRoles) {
    if (user?.id) {
      const response = await client.mutate({
        mutation: gql`
          mutation ($userId: String!, $availableRoles: [ObjectRef!]!, $selectedRoles: [ObjectRef!]!) {
            updateUserRoles(userId: $userId, availableRoles: $availableRoles, selectedRoles: $selectedRoles) {
              id
              name
              roles {
                id
                description
              }
            }
          }
        `,
        variables: { userId: user.id, availableRoles, selectedRoles },
        context: gqlContext()
      })

      if (!response.errors) {
        this.showToast(i18next.t('text.data_updated_successfully'))
        this.dispatchUserUpdated()
      }
    }
  }

  async onActivate(user) {
    const response = await client.mutate({
      mutation: gql`
        mutation activateUser($userId: String!) {
          activateUser(userId: $userId)
        }
      `,
      variables: { userId: user.id },
      context: gqlContext()
    })

    if (!response.errors) {
      this.showToast(i18next.t('text.user activated successfully'))

      this.dispatchUserUpdated()
    }
  }

  async onInactivate(user) {
    const response = await client.mutate({
      mutation: gql`
        mutation inactivateUser($userId: String!) {
          inactivateUser(userId: $userId)
        }
      `,
      variables: { userId: user.id },
      context: gqlContext()
    })

    if (!response.errors) {
      this.showToast(i18next.t('text.user activated successfully'))

      this.dispatchUserUpdated()
    }
  }

  async onDelete(user) {
    if (user.owner) {
      await OxPrompt.open({
        type: 'warning',
        title: i18next.t('text.cannot_delete'),
        text: i18next.t('text.x_cannot_delete', {
          x: i18next.t('label.domain-owner')
        }),
        confirmButton: { text: i18next.t('button.confirm') }
      })

      return
    }

    if (
      await OxPrompt.open({
        title: i18next.t('text.are_you_sure'),
        text: i18next.t('text.are_you_sure_to_x_user', {
          x: i18next.t('button.delete')
        }),
        confirmButton: { text: i18next.t('button.delete') },
        cancelButton: { text: i18next.t('button.cancel') }
      })
    ) {
      const response = await client.mutate({
        mutation: gql`
          mutation deleteDomaineUser($email: EmailAddress!) {
            deleteDomainUser(email: $email)
          }
        `,
        variables: { email: user.email },
        context: gqlContext()
      })

      if (!response.errors) {
        await OxPrompt.open({
          type: 'success',
          title: i18next.t('text.completed'),
          confirmButton: { text: i18next.t('button.confirm') }
        })

        this.dispatchUserUpdated()
      }
    }
  }

  async onTransfer(user) {
    openPopup(
      html`
        <ownership-transfer-popup
          .user=${user}
          @ownershipTransferred="${this.dispatchOwnershipTransferred.bind(this)}"
        ></ownership-transfer-popup>
      `,
      {
        size: 'small',
        title: `${i18next.t('title.owner_transfer')}`
      }
    )
  }

  async onResetPassword(user) {
    if (
      await OxPrompt.open({
        title: i18next.t('text.are_you_sure'),
        text: i18next.t('text.are_you_sure_to_x', { x: i18next.t('title.reset password') }),
        confirmButton: { text: i18next.t('button.confirm') },
        cancelButton: { text: i18next.t('button.cancel') }
      })
    ) {
      const response = await client.mutate({
        mutation: gql`
          mutation resetPasswordToDefault($userId: String!) {
            resetPasswordToDefault(userId: $userId)
          }
        `,
        variables: { userId: user.id },
        context: gqlContext()
      })

      if (!response.errors) {
        await OxPrompt.open({
          type: 'success',
          title: i18next.t('text.completed'),
          text: i18next.t('text.password reset succeed'),
          confirmButton: { text: i18next.t('button.confirm') }
        })
      }
    }
  }

  dispatchUserUpdated() {
    this.dispatchEvent(new CustomEvent('userUpdated'))
  }

  dispatchOwnershipTransferred() {
    this.dispatchEvent(new CustomEvent('ownershipTransferred'))
  }

  showToast(message) {
    document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1000 } } }))
  }

  stateChanged(state) {
    this.me = state.auth?.user || {}
    this.isSuperOwner = this.me.super || false
    this.isDomainOwner = this.me.email === this.domainOwner.email || false
  }
}
