import '@operato/data-grist'
import '@operato/context/ox-context-page-toolbar.js'

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

import { getEditor, getRenderer, ColumnConfig, DataGrist, FetchOption, SortersControl } from '@operato/data-grist'
import { client } from '@operato/graphql'
import { i18next, localize } from '@operato/i18n'
import { notify, openPopup } from '@operato/layout'
import { PageView, store } from '@operato/shell'
import { CommonButtonStyles, CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
import { isMobileDevice, sleep } from '@operato/utils'
import { OxPopup } from '@operato/popup'
import { OxPrompt } from '@operato/popup/ox-prompt.js'

@customElement('auth-provider-management')
export class AuthProviderManagementPage extends connect(store)(localize(i18next)(PageView)) {
  static styles = [
    ScrollbarStyles,
    CommonHeaderStyles,
    css`
      :host {
        display: flex;

        width: 100%;

        --grid-record-emphasized-background-color: #8b0000;
        --grid-record-emphasized-color: #ff6b6b;
      }

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

  @state() gristConfig: any
  @state() mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
  @state() authProviderTypes: { type: string; description?: string; help?: string; parameterSpec?: any[] }[] = []

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

  get context() {
    return {
      title: i18next.t('title.auth-provider'),
      search: {
        handler: (search: string) => {
          this.grist.searchText = search
        },
        value: this.grist?.searchText || ''
      },
      filter: {
        handler: () => {
          this.grist.toggleHeadroom()
        }
      },
      // help: 'auth/auth-provider',
      actions: [
        {
          title: i18next.t('button.save'),
          action: this.updateAuthProvider.bind(this),
          icon: 'save'
        },
        {
          title: i18next.t('button.delete'),
          action: this.deleteAuthProvider.bind(this),
          icon: 'delete'
        }
      ],
      toolbar: false
    }
  }

  render() {
    const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')

    return html`
      <ox-grist
        .mode=${mode}
        .config=${this.gristConfig}
        .fetchHandler=${this.fetchHandler.bind(this)}
        ?url-params-sensitive=${this.active}
      >
        <div slot="headroom" class="header">
          <div class="title">
            <mwc-icon>summarize</mwc-icon>
            ${i18next.t('title.auth-provider')}
          </div>

          <ox-filters-form autofocus without-search class="filters"></ox-filters-form>

          <ox-context-page-toolbar class="actions" .context=${this.context}> </ox-context-page-toolbar>
        </div>
      </ox-grist>
    `
  }

  async pageInitialized(lifecycle) {
    this.gristConfig = {
      list: {
        fields: ['entity', 'description']
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        { type: 'gutter', gutterName: 'row-selector', multiple: true },
        {
          type: 'gutter',
          gutterName: 'button',
          title: i18next.t('title.auth-provider'),
          icon: 'sync',
          handlers: {
            click: (columns, data, column, record, rowIndex) => {
              if (!record.id) return
              this.synchronizeUsers(record.id)
            }
          }
        },
        {
          type: 'select',
          name: 'type',
          header: i18next.t('field.type'),
          record: {
            editable: true,
            options: () => ['', ...this.authProviderTypes.map(({ type }) => type)]
          },
          filter: 'search',
          sortable: true,
          width: 150
        },
        {
          type: 'boolean',
          name: 'active',
          header: i18next.t('field.active'),
          record: {
            editable: true
          },
          sortable: true,
          width: 150
        },
        {
          type: 'string',
          name: 'tenantId',
          header: i18next.t('field.tenant-id'),
          record: {
            editable: true
          },
          width: 200
        },
        {
          type: 'string',
          name: 'clientId',
          header: i18next.t('field.client-id'),
          record: {
            editable: true
          },
          width: 200
        },
        {
          type: 'string',
          name: 'clientSecret',
          header: i18next.t('field.client-secret'),
          record: {
            editable: true
          },
          width: 200
        },
        {
          type: 'string',
          name: 'privateKey',
          header: i18next.t('field.private-key'),
          record: {
            editable: true
          },
          width: 200
        },
        {
          type: 'parameters',
          name: 'params',
          header: i18next.t('field.params'),
          record: {
            editable: true,
            options: async (value, column, record, row, field) => {
              const {
                description,
                help,
                parameterSpec: spec
              } = record.type ? this.authProviderTypes[record.type] : ({} as any)
              const context = this.grist

              return { description, help, spec, context }
            },
            renderer: 'json5'
          },
          width: 200
        },
        {
          type: 'datetime',
          name: 'updatedAt',
          header: i18next.t('field.updated_at'),
          record: {
            editable: false
          },
          sortable: true,
          width: 180
        }
      ],
      rows: {
        selectable: {
          multiple: true
        }
      },
      sorters: [
        {
          name: 'type'
        }
      ]
    }
  }

  async fetchHandler({ page, limit, sortings = [], filters = [] }: FetchOption) {
    const response = await client.query({
      query: gql`
        query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
          authProviderTypes {
            items {
              type
              description
              help
              parameterSpec {
                type
                label
                name
                placeholder
                property
              }
            }
            total
          }

          responses: authProviders(filters: $filters, pagination: $pagination, sortings: $sortings) {
            items {
              id
              type
              active
              tenantId
              clientId
              clientSecret
              privateKey
              params
              updatedAt
            }
            total
          }
        }
      `,
      variables: {
        filters,
        pagination: { page, limit },
        sortings
      }
    })

    this.authProviderTypes = response.data.authProviderTypes.items

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

  private async deleteAuthProvider() {
    if (
      await OxPrompt.open({
        title: i18next.t('text.are_you_sure'),
        text: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
        confirmButton: { text: i18next.t('button.confirm') },
        cancelButton: { text: i18next.t('button.cancel') }
      })
    ) {
      const ids = this.grist.selected.map(record => record.id)
      if (ids && ids.length > 0) {
        const response = await client.mutate({
          mutation: gql`
            mutation ($ids: [String!]!) {
              deleteAuthProviders(ids: $ids)
            }
          `,
          variables: {
            ids
          }
        })

        if (!response.errors) {
          this.grist.fetch()
          notify({
            message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') })
          })
        }
      }
    }
  }

  private async updateAuthProvider() {
    let patches = this.grist.dirtyRecords
    if (patches && patches.length) {
      patches = patches.map(patch => {
        let patchField: any = patch.id ? { id: patch.id } : {}
        const dirtyFields = patch.__dirtyfields__
        for (let key in dirtyFields) {
          patchField[key] = dirtyFields[key].after
        }
        if (patchField['reportTemplate'] instanceof FileList) {
          patchField['reportTemplate'] = patchField['reportTemplate'][0]
        }
        patchField.cuFlag = patch.__dirty__

        return patchField
      })

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

      if (!response.errors) {
        this.grist.fetch()
      }
    }
  }

  private async synchronizeUsers(id: string) {
    if (
      await OxPrompt.open({
        title: i18next.t('text.are_you_sure'),
        text: i18next.t('text.sure_to_x', { x: i18next.t('text.synchronize') }),
        confirmButton: { text: i18next.t('button.confirm') },
        cancelButton: { text: i18next.t('button.cancel') }
      })
    ) {
      if (id && id.length > 0) {
        const response = await client.mutate({
          mutation: gql`
            mutation ($id: String!) {
              synchronizeAuthProviderUsers(id: $id)
            }
          `,
          variables: {
            id
          }
        })

        if (!response.errors) {
          this.grist.fetch()
          notify({
            message: i18next.t('text.info_x_successfully', { x: i18next.t('text.synchronize') })
          })
        }
      }
    }
  }
}
