<template>
  <div class="page-editor">
    <PageHeader :page="page" />
    <div class="layout__col-2-2 page-editor__content">
      <div :style="{ width: `calc(100% - ${panelWidth}px)` }">
        <PagePreview />
      </div>
      <div
        class="page-editor__side-panel"
        :style="{ width: `${panelWidth}px` }"
      >
        <PageSidePanels />
      </div>
    </div>
  </div>
</template>

<script>
import { StoreItemLookupError } from '@baserow/modules/core/errors'
import PageHeader from '@baserow/modules/builder/components/page/header/PageHeader'
import PagePreview from '@baserow/modules/builder/components/page/PagePreview'
import PageSidePanels from '@baserow/modules/builder/components/page/PageSidePanels'
import { DataProviderType } from '@baserow/modules/core/dataProviderTypes'
import { BuilderApplicationType } from '@baserow/modules/builder/applicationTypes'
import ApplicationBuilderFormulaInputGroup from '@baserow/modules/builder/components/ApplicationBuilderFormulaInputGroup'
import _ from 'lodash'

const mode = 'editing'

export default {
  name: 'PageEditor',
  components: { PagePreview, PageHeader, PageSidePanels },
  provide() {
    return {
      workspace: this.workspace,
      builder: this.builder,
      page: this.page,
      mode,
      formulaComponent: ApplicationBuilderFormulaInputGroup,
      applicationContext: this.applicationContext,
    }
  },
  /**
   * When the route is updated we want to unselect the element
   */
  beforeRouteUpdate(to, from, next) {
    // Unselect previously selected element
    this.$store.dispatch('element/select', {
      element: null,
    })
    if (from.params.builderId !== to.params?.builderId) {
      // When we switch from one application to another we want to logoff the current
      // user
      const builder = this.$store.getters['application/get'](
        from.params.builderId
      )
      this.$store.dispatch('userSourceUser/logoff', { application: builder })
    }
    next()
  },
  /**
   * When the user leaves to another page we want to unselect the selected page. This
   * way it will not be highlighted the left sidebar.
   */
  beforeRouteLeave(to, from, next) {
    this.$store.dispatch('page/unselect')
    // Unselect previously selected element
    this.$store.dispatch('element/select', {
      element: null,
    })

    const builder = this.$store.getters['application/get'](
      from.params.builderId
    )
    this.$store.dispatch('userSourceUser/logoff', { application: builder })
    next()
  },
  layout: 'app',
  async asyncData({ store, params, error, $registry }) {
    const builderId = parseInt(params.builderId)
    const pageId = parseInt(params.pageId)

    const data = { panelWidth: 360 }

    try {
      const builder = await store.dispatch('application/selectById', builderId)
      store.dispatch('userSourceUser/setCurrentApplication', {
        application: builder,
      })

      const workspace = await store.dispatch(
        'workspace/selectById',
        builder.workspace.id
      )

      const builderApplicationType = $registry.get(
        'application',
        BuilderApplicationType.getType()
      )
      await builderApplicationType.loadExtraData(builder)

      const page = store.getters['page/getById'](builder, pageId)

      await Promise.all([
        store.dispatch('dataSource/fetch', {
          page,
        }),
        store.dispatch('element/fetch', { page }),
        store.dispatch('workflowAction/fetch', { page }),
      ])

      await DataProviderType.initAll($registry.getAll('builderDataProvider'), {
        builder,
        page,
        mode,
      })

      // And finally select the page to display it
      await store.dispatch('page/selectById', {
        builder,
        pageId,
      })

      data.workspace = workspace
      data.builder = builder
      data.page = page
    } catch (e) {
      // In case of a network error we want to fail hard.
      if (e.response === undefined && !(e instanceof StoreItemLookupError)) {
        throw e
      }

      return error({ statusCode: 404, message: 'page not found.' })
    }

    return data
  },
  computed: {
    applicationContext() {
      return {
        builder: this.builder,
        page: this.page,
        mode,
      }
    },
    dataSources() {
      return this.$store.getters['dataSource/getPageDataSources'](this.page)
    },
    dispatchContext() {
      return DataProviderType.getAllDataSourceDispatchContext(
        this.$registry.getAll('builderDataProvider'),
        this.applicationContext
      )
    },
  },
  watch: {
    dataSources: {
      deep: true,
      /**
       * Update data source content on data source configuration changes
       */
      handler() {
        this.$store.dispatch(
          'dataSourceContent/debouncedFetchPageDataSourceContent',
          {
            page: this.page,
            data: this.dispatchContext,
          }
        )
      },
    },
    dispatchContext: {
      deep: true,
      /**
       * Update data source content on backend context changes
       */
      handler(newDispatchContext, oldDispatchContext) {
        if (!_.isEqual(newDispatchContext, oldDispatchContext)) {
          this.$store.dispatch(
            'dataSourceContent/debouncedFetchPageDataSourceContent',
            {
              page: this.page,
              data: newDispatchContext,
            }
          )
        }
      },
    },
  },
}
</script>