import '@material/web/icon/icon.js'

import '@operato/data-grist/ox-grist.js'
import '@operato/data-grist/ox-filters-form.js'
import '@operato/context/ox-context-page-toolbar.js'
import '@operato/attribute/ox-attribute-view.js'
import '../../components/create-domain-popup'

import gql from 'graphql-tag'
import { css, html } from 'lit'
import { customElement, property, query } from 'lit/decorators.js'
import moment from '@operato/moment-timezone-es'
import { connect } from 'pwa-helpers/connect-mixin'

import { DataGrist } from '@operato/data-grist/ox-grist.js'
import { client } from '@operato/graphql'
import { i18next, localize } from '@operato/i18n'
import { openPopup } from '@operato/layout'
import { PageView, store } from '@operato/shell'
import { CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
import { isMobileDevice } from '@operato/utils'
import { p13n } from '@operato/p13n'
import { FetchOption } from '@operato/data-grist'

function getSystemTimeZone(): string {
  try {
    const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
    if (!timeZone) {
      throw new Error('Unable to resolve timeZone')
    }
    return timeZone
  } catch (e) {
    console.warn('Failed to get system timeZone, falling back to UTC.', e)
    return 'UTC'
  }
}

const DEFAULT_TZ = getSystemTimeZone()
const TIMEZONE_OPTIONS = ['', DEFAULT_TZ, ...moment.tz.names().filter(tz => tz !== DEFAULT_TZ)]

@customElement('domain-management')
export class DomainManagement extends connect(store)(p13n(localize(i18next)(PageView))) {
  static styles = [
    CommonGristStyles,
    CommonHeaderStyles,
    ScrollbarStyles,
    css`
      :host {
        display: flex;
        flex-direction: column;

        overflow: hidden;
      }

      ox-grist {
        overflow-y: auto;
        flex: 1;
      }
    `
  ]

  @property({ type: Boolean }) active: boolean = false
  @property({ type: Object }) config: any
  @property({ type: String }) mode: 'CARD' | 'LIST' | 'GRID' = isMobileDevice() ? 'CARD' : 'GRID'

  private domainTypes?: string[]

  @query('ox-grist') private grist!: DataGrist

  get context() {
    return {
      title: i18next.t('text.domain management'),
      search: {
        handler: (search: string) => {
          this.grist.searchText = search
        },
        value: this.grist?.searchText || ''
      },
      filter: {
        handler: () => {
          this.grist.toggleHeadroom()
        }
      },
      actions: [
        {
          title: i18next.t('button.add'),
          action: this.onCreateDomain.bind(this),
          icon: 'add'
        },
        {
          title: i18next.t('button.save'),
          action: this.onUpdateDomains.bind(this),
          icon: 'save'
        }
      ],
      toolbar: false
    }
  }

  render() {
    return html`
      <ox-grist
        .mode=${this.mode}
        .config=${this.config}
        .fetchHandler=${this.fetchHandler.bind(this)}
        .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}
      >
        <div slot="headroom" class="header">
          <ox-context-page-toolbar class="actions" .context=${this.context}></ox-context-page-toolbar>
        </div>

        <ox-grist-personalizer slot="setting"></ox-grist-personalizer>
      </ox-grist>
    `
  }

  async pageInitialized() {
    this.config = {
      rows: {
        appendable: false
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'assignment',
          title: i18next.t('title.open attributes view'),
          handlers: {
            click: async (columns, data, column, record, rowIndex) => {
              const attributeSet = await this.getAttributeSetForDomain()

              openPopup(
                html`
                  <ox-attribute-view
                    .attributeSet=${attributeSet}
                    .value=${record.attributes}
                    style="background-color: white;"
                  ></ox-attribute-view>
                `,
                {
                  backdrop: true,
                  size: 'large',
                  title: record.name + ' ' + i18next.t('title.attributes')
                }
              )
            }
          }
        },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'domain',
          title: i18next.t('title.move to this domain'),
          handlers: {
            click: (_columns, _data, _column, record, _rowIndex) => {
              if (record.id) {
                const subdomain = record.subdomain
                const extType = record.extType || 'domain'
                location.pathname = `/${extType}/${subdomain}/users`
              }
            }
          }
        },
        {
          type: 'string',
          name: 'name',
          header: i18next.t('field.name'),
          record: {
            editable: false
          },
          filter: 'search',
          sortable: true,
          width: 200
        },
        {
          type: 'string',
          name: 'description',
          header: i18next.t('field.description'),
          record: {
            editable: true
          },
          filter: 'search',
          width: 200
        },
        {
          type: 'string',
          name: 'subdomain',
          header: i18next.t('field.subdomain'),
          record: {
            editable: false
          },
          filter: 'search',
          width: 200
        },
        {
          type: 'resource-object',
          name: 'parent',
          header: i18next.t('field.parent-domain'),
          record: {
            editable: true,
            options: {
              title: i18next.t('text.select domain'),
              queryName: 'domains',
              nameField: 'name',
              descriptionField: 'description',
              select: [
                { name: 'id', hidden: true },
                { name: 'name', header: i18next.t('field.name'), filter: 'search' },
                { name: 'description', header: i18next.t('field.description'), filter: 'search' }
              ],
              list: { fields: ['name', 'description'] }
            }
          },
          width: 140
        },
        {
          type: 'resource-object',
          name: 'ownerUser',
          header: i18next.t('field.owner-user'),
          record: {
            editable: true,
            options: {
              title: i18next.t('title.lookup user'),
              queryName: 'users',
              nameField: 'name',
              descriptionField: 'email',
              select: [
                { name: 'id', hidden: true },
                { name: 'name', header: i18next.t('field.name'), filter: 'search' },
                { name: 'email', header: i18next.t('field.email'), filter: 'search' }
              ],
              filters: [{ name: 'userType', operator: 'eq', value: 'user' }],
              list: { fields: ['name', 'email'] }
            }
          },
          width: 140
        },
        {
          type: 'select',
          name: 'extType',
          header: i18next.t('field.type'),
          record: {
            editable: true,
            options: async () => {
              return ['', ...(await this.getDomainTypes())]
            }
          },
          width: 100
        },
        {
          type: 'select',
          name: 'timezone',
          header: i18next.t('field.timezone'),
          record: {
            editable: true,
            options: TIMEZONE_OPTIONS
          },
          width: 120
        },
        {
          type: 'attributes',
          name: 'attributes',
          header: i18next.t('field.attributes'),
          record: {
            editable: true,
            options: async () => {
              return {
                objectified: true,
                attributeSet: await this.getAttributeSetForDomain()
              }
            }
          },
          width: 120
        },
        {
          type: 'datetime',
          name: 'updatedAt',
          header: i18next.t('field.updated_at'),
          width: 180
        }
      ]
    }
  }

  async pageUpdated(changes, lifecycle) {
    if (this.active) {
      await this.updateComplete

      this.grist.fetch()
    }
  }

  async fetchHandler({ page, limit, sortings = [], filters = [] }: FetchOption) {
    const response = await client.query({
      query: gql`
        query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
          domains(filters: $filters, pagination: $pagination, sortings: $sortings) {
            items {
              id
              name
              description
              subdomain
              parent {
                id
                name
              }
              ownerUser {
                id
                name
                email
              }
              extType
              timezone
              attributes
              updatedAt
            }
            total
          }
        }
      `,
      variables: {
        filters,
        pagination: { page, limit },
        sortings
      }
    })

    return {
      total: response.data.domains.total || 0,
      records: response.data.domains.items || []
    }
  }

  async onUpdateDomains() {
    let patches = this.grist.dirtyRecords

    if (patches && patches.length) {
      patches = patches.map(domain => {
        let patchField = domain.id ? { id: domain.id } : {}
        const dirtyFields = domain.__dirtyfields__
        for (let key in dirtyFields) {
          if (key == 'ownerUser') {
            const ownerUser = dirtyFields[key].after
            patchField['owner'] = ownerUser.id || ''
          } else {
            patchField[key] = dirtyFields[key].after
          }
        }

        return {
          ...patchField
        }
      })

      const response = await client.mutate({
        mutation: gql`
          mutation updateDomains($patches: [DomainPatch!]!) {
            updateDomains(patches: $patches)
          }
        `,
        variables: { patches }
      })

      if (!response.errors) {
        this.grist.fetch()
        return this.showToast(i18next.t('text.updated_successfully'))
      }
    }
  }

  async onCreateDomain() {
    openPopup(
      html`
        <create-domain-popup
          @fetch-data=${async () => {
            this.grist.fetch()
          }}
        ></create-domain-popup>
      `,
      {
        size: 'small',
        title: `${i18next.t('title.create_domain')}`
      }
    )
  }

  async getAttributeSetForDomain() {
    const response = await client.query({
      query: gql`
        query {
          attributeSet: attributeSetByEntity(entity: "Domain") {
            entity
            description
            items {
              name
              description
              tag
              type
              active
              hidden
              options
            }
          }
        }
      `
    })

    return response.data?.attributeSet || {}
  }

  async getDomainTypes() {
    if (!this.domainTypes) {
      const response = await client.query({
        query: gql`
          query {
            domainTypes
          }
        `
      })

      this.domainTypes = response.data?.domainTypes || ['domain']
    }

    return this.domainTypes || []
  }

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