import { computed, defineComponent, h, ref, watch } from 'vue'

import { CPagination, CPaginationItem } from './../pagination'

const CSmartPagination = defineComponent({
  name: 'CSmartPagination',
  props: {
    /**
     * Horizontall align
     *
     * @default 'start'
     */
    align: {
      type: String,
      default: 'start',
      validator: (value: string) => {
        return ['start', 'center', 'end'].includes(value)
      },
    },
    /**
     * Current page number
     *
     * @default 1
     */
    activePage: {
      type: Number,
      default: 1,
    },
    /**
     * Show/hide arrows
     *
     * @default true
     */
    arrows: {
      type: Boolean,
      default: true,
    },
    /**
     * Show/hide dots
     *
     * @default true
     */
    dots: {
      type: Boolean,
      default: true,
    },

    /**
     * Show double arrows buttons
     *
     * @default true
     */
    doubleArrows: {
      type: Boolean,
      default: true,
    },
    /**
     * The content of 'firstButton' button
     *
     * @default '&laquo;'
     */
    firstButton: {
      type: String,
      default: '&laquo;',
    },
    /**
     * The content of 'lastButton' button
     *
     * @default '&raquo;'
     */
    lastButton: {
      type: String,
      default: '&raquo;',
    },
    /**
     * Maximum items number
     *
     * @default 5
     */
    limit: {
      type: Number,
      default: 5,
    },
    /**
     * The content of 'nextButton' button
     *
     * @default '&rsaquo;'
     */
    nextButton: {
      type: String,
      default: '&rsaquo;',
    },
    /**
     * Number of pages
     */
    pages: {
      type: Number,
      default: 1000,
    },
    /**
     * The content of 'previousButton' button
     *
     * @default '&lsaquo;'
     */
    previousButton: {
      type: String,
      default: '&lsaquo;',
    },
    /**
     * Size of pagination, valid values: 'sm', 'lg'
     */
    size: {
      type: String,
      default: undefined,
      required: false,
      validator: (value: string) => {
        return ['sm', 'lg'].includes(value)
      },
    },
  },
  emits: [
    /**
     * On active page change callback.
     */
    'activePageChange',
  ],
  setup(props, { emit }) {
    const activePage = ref(props.activePage)
    const limit = ref(props.limit)
    const pages = ref(props.pages)

    watch(
      () => props.activePage,
      () => {
        activePage.value = props.activePage
      },
    )

    watch(
      () => props.limit,
      () => {
        limit.value = props.limit
      },
    )

    watch(
      () => props.pages,
      () => {
        pages.value = props.pages
      },
    )

    const showDots = computed(() => {
      return props.dots && limit.value > 4 && limit.value < pages.value
    })

    const maxPrevItems = computed(() => {
      return Math.floor((limit.value - 1) / 2)
    })

    const maxNextItems = computed(() => {
      return Math.ceil((limit.value - 1) / 2)
    })

    const beforeDots = computed(() => {
      return showDots.value && activePage.value > maxPrevItems.value + 1
    })

    const afterDots = computed(() => {
      return showDots.value && activePage.value < pages.value - maxNextItems.value
    })

    const computedLimit = computed(() => {
      return limit.value - (afterDots.value ? 1 : 0) - (beforeDots.value ? 1 : 0)
    })

    const range = computed(() => {
      return activePage.value + maxNextItems.value
    })

    const lastItem = computed(() => {
      return range.value >= pages.value ? pages.value : range.value - (afterDots.value ? 1 : 0)
    })

    const itemsAmount = computed(() => {
      return pages.value < computedLimit.value ? pages.value : computedLimit.value
    })

    const items = computed(() => {
      if (activePage.value - maxPrevItems.value <= 1) {
        return Array.from(
          {
            length: itemsAmount.value,
          },
          (_v, i) => i + 1,
        )
      } else {
        return Array.from(
          {
            length: itemsAmount.value,
          },
          (_v, i) => {
            return lastItem.value - i
          },
        ).reverse()
      }
    })

    const setPage = (number: number): void => {
      if (number !== activePage.value) {
        activePage.value = number
        emit('activePageChange', number)
      }
    }

    return () =>
      h(
        CPagination,
        {
          align: props.align,
          'aria-label': 'pagination',
          size: props.size,
        },
        {
          default: () => [
            props.doubleArrows &&
              h(
                CPaginationItem,
                {
                  onClick: () => {
                    activePage.value !== 1 && setPage(1)
                  },
                  'aria-label': 'Go to first page',
                  ...(activePage.value === 1 && {
                    'aria-disabled': true,
                    disabled: true,
                  }),
                },
                {
                  default: () =>
                    typeof props.firstButton === 'string'
                      ? h('span', {
                          innerHTML: props.firstButton,
                        })
                      : props.firstButton,
                },
              ),
            props.arrows &&
              h(
                CPaginationItem,
                {
                  onClick: () => {
                    activePage.value !== 1 && setPage(activePage.value - 1)
                  },
                  'aria-label': 'Go to previous page',
                  ...(activePage.value === 1 && {
                    'aria-disabled': true,
                    disabled: true,
                  }),
                },
                {
                  default: () =>
                    typeof props.previousButton === 'string'
                      ? h('span', {
                          innerHTML: props.previousButton,
                        })
                      : props.previousButton,
                },
              ),
            beforeDots.value &&
              h(
                CPaginationItem,
                {
                  role: 'separator',
                  disabled: true,
                },
                {
                  default: () => '...',
                },
              ),
            items.value.map((i: number) => {
              return h(
                CPaginationItem,
                {
                  onClick: () => setPage(i),
                  'aria-label': activePage.value === i ? `Current page ${i}` : `Go to page ${i}`,
                  active: activePage.value === i,
                },
                {
                  default: () => i,
                },
              )
            }),
            afterDots.value &&
              h(
                CPaginationItem,
                {
                  role: 'separator',
                  disabled: true,
                },
                {
                  default: () => '...',
                },
              ),
            props.arrows &&
              h(
                CPaginationItem,
                {
                  onClick: () => {
                    activePage.value !== pages.value && setPage(activePage.value + 1)
                  },
                  'aria-label': 'Go to next page',
                  ...(activePage.value === pages.value && {
                    'aria-disabled': true,
                    disabled: true,
                  }),
                },
                {
                  default: () =>
                    typeof props.nextButton === 'string'
                      ? h('span', {
                          innerHTML: props.nextButton,
                        })
                      : props.nextButton,
                },
              ),
            props.doubleArrows &&
              h(
                CPaginationItem,
                {
                  onClick: () => {
                    activePage.value !== pages.value && setPage(pages.value)
                  },
                  'aria-label': 'Go to last page',
                  ...(activePage.value === pages.value && {
                    'aria-disabled': true,
                    disabled: true,
                  }),
                },
                {
                  default: () =>
                    typeof props.lastButton === 'string'
                      ? h('span', {
                          innerHTML: props.lastButton,
                        })
                      : props.lastButton,
                },
              ),
          ],
        },
      )
  },
})

export { CSmartPagination }
