import { defineStore } from 'pinia'
import gql from 'graphql-tag'
import { cloneDeep, dropRight, initial, last, pick, transform } from 'lodash-es'
import { DateTime } from 'luxon'

import { useSiteStore } from './site'
import { useEditorStore } from './editor'

const pagePropsFragment = gql`
  fragment PageRead on Page {
    alias
    allowComments
    allowContributions
    allowRatings
    contentType
    createdAt
    description
    editor
    icon
    id
    isBrowsable
    locale
    password
    path
    publishEndDate
    publishStartDate
    publishState
    relations {
      id
      position
      label
      caption
      icon
      target
    }
    render
    scriptJsLoad
    scriptJsUnload
    scriptCss
    showSidebar
    showTags
    showToc
    tags {
      tag
      title
    }
    title
    toc
    tocDepth {
      min
      max
    }
    updatedAt
  }
`
const gqlQueries = {
  pageById: gql`
    query loadPage (
      $id: UUID!
    ) {
      pageById(
        id: $id
      ) {
        ...PageRead
      }
    }
    ${pagePropsFragment}
  `,
  pageByPath: gql`
    query loadPage (
      $siteId: UUID!
      $path: String!
    ) {
      pageByPath(
        siteId: $siteId
        path: $path
      ) {
        ...PageRead
      }
    }
    ${pagePropsFragment}
  `,
  pageByIdWithContent: gql`
    query loadPageWithContent (
      $id: UUID!
    ) {
      pageById(
        id: $id
      ) {
        ...PageRead,
        content
      }
    }
    ${pagePropsFragment}
  `,
  pageByPathWithContent: gql`
    query loadPageWithContent (
      $siteId: UUID!
      $path: String!
    ) {
      pageByPath(
        siteId: $siteId
        path: $path
      ) {
        ...PageRead,
        content
      }
    }
    ${pagePropsFragment}
  `
}

const gqlMutations = {
  createPage: gql`
    mutation createPage (
      $alias: String
      $allowComments: Boolean
      $allowContributions: Boolean
      $allowRatings: Boolean
      $content: String!
      $description: String!
      $editor: String!
      $icon: String
      $isBrowsable: Boolean
      $locale: String!
      $path: String!
      $publishState: PagePublishState!
      $publishEndDate: Date
      $publishStartDate: Date
      $relations: [PageRelationInput!]
      $scriptCss: String
      $scriptJsLoad: String
      $scriptJsUnload: String
      $showSidebar: Boolean
      $showTags: Boolean
      $showToc: Boolean
      $siteId: UUID!
      $tags: [String!]
      $title: String!
      $tocDepth: PageTocDepthInput
    ) {
      createPage (
        alias: $alias
        allowComments: $allowComments
        allowContributions: $allowContributions
        allowRatings: $allowRatings
        content: $content
        description: $description
        editor: $editor
        icon: $icon
        isBrowsable: $isBrowsable
        locale: $locale
        path: $path
        publishState: $publishState
        publishEndDate: $publishEndDate
        publishStartDate: $publishStartDate
        relations: $relations
        scriptCss: $scriptCss
        scriptJsLoad: $scriptJsLoad
        scriptJsUnload: $scriptJsUnload
        showSidebar: $showSidebar
        showTags: $showTags
        showToc: $showToc
        siteId: $siteId
        tags: $tags
        title: $title
        tocDepth: $tocDepth
      ) {
        operation {
          succeeded
          message
        }
        page {
          ...PageRead
        }
      }
    }
    ${pagePropsFragment}
  `
}

export const usePageStore = defineStore('page', {
  state: () => ({
    alias: '',
    allowComments: false,
    allowContributions: true,
    allowRatings: true,
    authorId: 0,
    authorName: '',
    commentsCount: 0,
    content: '',
    createdAt: '',
    description: '',
    editor: '',
    icon: 'las la-file-alt',
    id: '',
    isBrowsable: true,
    locale: 'en',
    password: '',
    path: '',
    publishEndDate: '',
    publishStartDate: '',
    publishState: '',
    relations: [],
    render: '',
    scriptJsLoad: '',
    scriptJsUnload: '',
    scriptCss: '',
    showSidebar: true,
    showTags: true,
    showToc: true,
    tags: [],
    title: '',
    toc: [],
    tocDepth: {
      min: 1,
      max: 2
    },
    updatedAt: ''
  }),
  getters: {
    breadcrumbs: (state) => {
      const siteStore = useSiteStore()
      const pathPrefix = siteStore.useLocales ? `/${state.locale}` : ''
      return transform(state.path.split('/'), (result, value, key) => {
        result.push({
          id: key,
          title: value,
          icon: 'las la-file-alt',
          locale: 'en',
          path: (last(result)?.path || pathPrefix) + `/${value}`
        })
      }, [])
    },
    folderPath: (state) => {
      return initial(state.path.split('/')).join('/')
    }
  },
  actions: {
    /**
     * PAGE - LOAD
     */
    async pageLoad ({ path, id, withContent = false }) {
      const editorStore = useEditorStore()
      const siteStore = useSiteStore()
      try {
        let query
        if (withContent) {
          query = id ? gqlQueries.pageByIdWithContent : gqlQueries.pageByPathWithContent
        } else {
          query = id ? gqlQueries.pageById : gqlQueries.pageByPath
        }
        const resp = await APOLLO_CLIENT.query({
          query,
          variables: id ? { id } : { siteId: siteStore.id, path },
          fetchPolicy: 'network-only'
        })
        const pageData = cloneDeep((id ? resp?.data?.pageById : resp?.data?.pageByPath) ?? {})
        if (!pageData?.id) {
          throw new Error('ERR_PAGE_NOT_FOUND')
        }
        // Update page store
        this.$patch({
          ...pageData,
          relations: pageData.relations.map(r => pick(r, ['id', 'position', 'label', 'caption', 'icon', 'target'])),
          tocDepth: pick(pageData.tocDepth, ['min', 'max'])
        })
        // Update editor state timestamps
        const curDate = DateTime.utc()
        editorStore.$patch({
          lastChangeTimestamp: curDate,
          lastSaveTimestamp: curDate
        })
      } catch (err) {
        console.warn(err)
        throw err
      }
    },
    /**
     * PAGE - GET PATH FROM ALIAS
     */
    async pageAlias (alias) {
      const siteStore = useSiteStore()
      try {
        const resp = await APOLLO_CLIENT.query({
          query: gql`
            query fetchPathFromAlias (
              $siteId: UUID!
              $alias: String!
            ) {
              pathFromAlias (
                siteId: $siteId
                alias: $alias
              ) {
                id
                path
              }
            }
          `,
          variables: { siteId: siteStore.id, alias },
          fetchPolicy: 'cache-first'
        })
        const pagePath = cloneDeep(resp?.data?.pathFromAlias)
        if (!pagePath?.id) {
          throw new Error('ERR_PAGE_NOT_FOUND')
        }
        return pagePath.path
      } catch (err) {
        console.warn(err)
        throw err
      }
    },
    /**
     * PAGE - CREATE
     */
    async pageCreate ({ editor, locale, path, title = '', description = '', content = '' }) {
      const editorStore = useEditorStore()

      if (!editorStore.configIsLoaded) {
        await editorStore.fetchConfigs()
      }

      const noDefaultPath = Boolean(!path && path !== '')

      // -> Init editor
      editorStore.$patch({
        originPageId: editorStore.isActive ? editorStore.originPageId : this.id, // Don't replace if already in edit mode
        isActive: true,
        mode: 'create',
        editor
      })

      // -> Default Page Path
      let newPath = path
      if (!path && path !== '') {
        const parentPath = dropRight(this.path.split('/'), 1).join('/')
        newPath = parentPath ? `${parentPath}/new-page` : 'new-page'
      }

      // -> Set Default Page Data
      this.$patch({
        id: 0,
        locale: locale || this.locale,
        path: newPath,
        title: title ?? '',
        description: description ?? '',
        icon: 'las la-file-alt',
        alias: '',
        publishState: 'published',
        relations: [],
        tags: [],
        content: content ?? '',
        render: '',
        mode: 'edit'
      })

      if (noDefaultPath) {
        this.router.push(`/_create/${editor}`)
      }
    },
    /**
     * PAGE - EDIT
     */
    async pageEdit () {
      const editorStore = useEditorStore()

      await this.pageLoad({ id: this.id, withContent: true })

      if (!editorStore.configIsLoaded) {
        await editorStore.fetchConfigs()
      }

      editorStore.$patch({
        isActive: true,
        mode: 'edit',
        editor: this.editor
      })
    },
    /**
     * PAGE SAVE
     */
    async pageSave () {
      const editorStore = useEditorStore()
      const siteStore = useSiteStore()
      try {
        if (editorStore.mode === 'create') {
          const resp = await APOLLO_CLIENT.mutate({
            mutation: gqlMutations.createPage,
            variables: {
              ...pick(this, [
                'alias',
                'allowComments',
                'allowContributions',
                'allowRatings',
                'content',
                'description',
                'icon',
                'isBrowsable',
                'locale',
                'password',
                'path',
                'publishEndDate',
                'publishStartDate',
                'publishState',
                'relations',
                'scriptJsLoad',
                'scriptJsUnload',
                'scriptCss',
                'showSidebar',
                'showTags',
                'showToc',
                'tags',
                'title',
                'tocDepth'
              ]),
              editor: editorStore.editor,
              siteId: siteStore.id
            }
          })
          const result = resp?.data?.createPage?.operation ?? {}
          if (!result.succeeded) {
            throw new Error(result.message)
          }
          const pageData = cloneDeep(resp.data.createPage.page ?? {})
          if (!pageData?.id) {
            throw new Error('ERR_CREATED_PAGE_NOT_FOUND')
          }
          // Update page store
          this.$patch({
            ...pageData,
            relations: pageData.relations.map(r => pick(r, ['id', 'position', 'label', 'caption', 'icon', 'target'])),
            tocDepth: pick(pageData.tocDepth, ['min', 'max'])
          })

          editorStore.$patch({
            mode: 'edit'
          })

          this.router.replace(`/${this.path}`)
        } else {
          const resp = await APOLLO_CLIENT.mutate({
            mutation: gql`
              mutation savePage (
                $id: UUID!
                $patch: PageUpdateInput!
              ) {
                updatePage (
                  id: $id
                  patch: $patch
                ) {
                  operation {
                    succeeded
                    message
                  }
                }
              }
              `,
            variables: {
              id: this.id,
              patch: {
                ...pick(this, [
                  'alias',
                  'allowComments',
                  'allowContributions',
                  'allowRatings',
                  'content',
                  'description',
                  'icon',
                  'isBrowsable',
                  'locale',
                  'password',
                  'path',
                  'publishEndDate',
                  'publishStartDate',
                  'publishState',
                  'relations',
                  'scriptJsLoad',
                  'scriptJsUnload',
                  'scriptCss',
                  'showSidebar',
                  'showTags',
                  'showToc',
                  'tags',
                  'title',
                  'tocDepth'
                ]),
                reasonForChange: editorStore.reasonForChange
              }
            }
          })
          const result = resp?.data?.updatePage?.operation ?? {}
          if (!result.succeeded) {
            throw new Error(result.message)
          }
        }
        // Update editor state timestamps
        const curDate = DateTime.utc()
        editorStore.$patch({
          lastChangeTimestamp: curDate,
          lastSaveTimestamp: curDate,
          reasonForChange: ''
        })
      } catch (err) {
        console.warn(err)
        throw err
      }
    },
    async cancelPageEdit () {
      const editorStore = useEditorStore()
      await this.pageLoad({ id: editorStore.originPageId ? editorStore.originPageId : this.id })
      this.router.replace(`/${this.path}`)
    },
    generateToc () {

    }
  }
})