import './things-scene-components.import'
import '@operato/board/ox-board-player.js'
import '@operato/oops'

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

import { createBoardProvider } from '../board-provider'
import { store, PageView } from '@operato/shell'
import { clientSettingStore } from '@operato/shell/object-store.js'
import { client, subscribe } from '@operato/graphql'
import { ReferenceMap } from '@hatiolab/things-scene'
import { BoardPlayer } from '@operato/board/ox-board-player.js'

function parseQuery(query: any) {
  for (const key in query) {
    if (query.hasOwnProperty(key)) {
      try {
        query[key] = JSON.parse(query[key])
      } catch (error) {
        // do nothing
      }
    }
  }

  return query
}

@customElement('board-player-page')
export class BoardPlayerPage extends connect(store)(PageView) {
  static styles = [
    css`
      :host {
        display: flex;
        flex-direction: column;
        width: 100%;
        height: 100%;

        overflow: hidden;
        position: relative;
      }

      ox-board-player {
        flex: 1;
      }

      ox-oops-spinner {
        display: none;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
      }

      ox-oops-spinner[show] {
        display: block;
      }

      ox-oops-note {
        display: block;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
      }
    `
  ]

  @state() data: any
  @state() playGroup: any
  @state() playGroupId?: string | null
  @state() boards: any
  @state() baseUrl?: string
  @state() showSpinner?: boolean

  private provider?: ReferenceMap<unknown> & { dispose?: () => void }
  private subscriptionForAutoRefresh

  @query('ox-board-player') boardPlayer?: BoardPlayer

  connectedCallback() {
    super.connectedCallback()

    this.provider = createBoardProvider()
  }

  disconnectedCallback() {
    super.disconnectedCallback()

    this.provider!.dispose!()
    delete this.provider
  }

  async fetch(id: string) {
    return await client.query({
      query: gql`
        query ($id: String!) {
          response: playGroup(id: $id) {
            id
            name
            description
            boards {
              id
              model
            }
          }
        }
      `,
      variables: {
        id
      }
    })
  }

  async refresh() {
    if (!this.playGroupId) {
      return
    }

    try {
      this.showSpinner = true
      this.updateContext()

      const response = await this.fetch(this.playGroupId)

      this.playGroup = response.data.response

      if (!this.playGroup) {
        throw 'playgroup not found'
      }

      this.boards = this.playGroup.boards
    } catch (ex) {
      document.dispatchEvent(
        new CustomEvent('notify', {
          detail: {
            level: 'error',
            message: ex,
            ex
          }
        })
      )
    } finally {
      this.showSpinner = false
      this.updateContext()

      const { autoRefresh = true } = (await clientSettingStore.get('board-view'))?.value || {}
      autoRefresh && this.startSubscribingForAutoRefresh()
    }
  }

  updated(changes) {
    if (changes.has('playGroupId')) {
      this.boardPlayer!.stop()
      this.refresh()
    }
  }

  stateChanged(state) {
    this.baseUrl = state.app.baseUrl
  }

  get oopsNote() {
    return {
      icon: 'style',
      title: 'EMPTY PLAYGROUP',
      description: 'There are no board to be shown'
    }
  }

  get context() {
    return {
      title: this.playGroup ? this.playGroup.name : this.showSpinner ? 'Fetching playgroup...' : 'Playgroup Not Found'
    }
  }

  render() {
    var oops = !this.showSpinner && !this.playGroup && this.oopsNote

    return oops
      ? html` <ox-oops-note icon=${oops.icon} title=${oops.title} description=${oops.description}></ox-oops-note> `
      : html`
          <ox-board-player .boards=${this.boards} .data=${this.data} .provider=${this.provider}></ox-board-player>
          <ox-oops-spinner ?show=${this.showSpinner}></ox-oops-spinner>
        `
  }

  pageUpdated(changes, lifecycle) {
    if (this.active) {
      this.playGroupId = lifecycle.resourceId
      this.data = parseQuery({ ...lifecycle.params })

      this.refresh()
    } else {
      this.stopSubscribing()

      this.playGroupId = null
      this.boardPlayer!.stop()
    }
  }

  async startSubscribingForAutoRefresh() {
    if (!this.playGroup) {
      return
    }

    await this.stopSubscribing()

    this.subscriptionForAutoRefresh = await subscribe(
      {
        query: gql`
          subscription ($id: String!) {
            playGroup(id: $id) {
              id
            }
          }
        `,
        variables: {
          id: this.playGroup.id
        }
      },
      {
        next: async ({ data }) => {
          await this.stopSubscribing()

          // location.reload()
          if (data) {
            this.refresh()
          }
        }
      }
    )
  }

  async stopSubscribing() {
    await this.subscriptionForAutoRefresh?.unsubscribe()
    delete this.subscriptionForAutoRefresh
  }
}
