/* eslint-disable no-param-reassign */
import _ from 'lodash'
import Vue from 'vue'
// import SntApp from '@/components/sale-page/components/App.vue'
// import SntContainer from '@/components/sale-page/components/Container.vue'
// import SntIcon from '@/components/sale-page/components/Icon.vue'
// import SntLink from '@/components/sale-page/components/Link.vue'
// import SntRow from '@/components/sale-page/components/Row.vue'
// import SntCol from '@/components/sale-page/components/Col.vue'
// import SntSection from '@/components/sale-page/components/Section.vue'
// import SntText from '@/components/sale-page/components/Text.vue'
// import SntTextEditor from '@/components/sale-page/components/TextEditor.vue'
// import SntNavbar from '@/components/sale-page/components/Navbar.vue'
// import SntAllProducts from '@/components/sale-page/components/AllProducts.vue'
// import SntProductDetail from '@/components/sale-page/components/ProductDetail.vue'
// import SntProducts from '@/components/sale-page/components/Products.vue'
// import SntCart from '@/components/sale-page/components/Cart.vue'
// import SntPayment from '@/components/sale-page/components/Payment.vue'
// import SntOrderDetail from '@/components/sale-page/components/OrderDetail.vue'
// import SntOrders from '@/components/sale-page/components/Orders.vue'
// import SntCheckout from '@/components/sale-page/components/Checkout.vue'
// import SntVideo from '@/components/sale-page/components/Video.vue'
// import SntImage from '@/components/sale-page/components/Image.vue'
// import SntFeed from '@/components/sale-page/components/Feed.vue'
// import SntFeeds from '@/components/sale-page/components/Feeds.vue'
// import SntCarousel from '@/components/sale-page/components/Carousel.vue'
// import SntFooter from '@/components/sale-page/components/Footer.vue'
// import SntCard from '@/components/sale-page/components/Card.vue'
// import SntTooltip from '@/components/sale-page/components/Tooltip.vue'
// import SntDialog from '@/components/sale-page/components/Dialog.vue'
// import SntButton from '@/components/sale-page/components/Button.vue'
// import SntHover from '@/components/sale-page/components/Hover.vue'
// import SntTextField from '@/components/sale-page/components/TextField.vue'
// import SntSelect from '@/components/sale-page/components/Select.vue'
// import SntShipping from '@/components/sale-page/components/Shipping.vue'
// import SntAddress from '@/components/sale-page/components/Address.vue'
// import SntPaymentMethods from '@/components/sale-page/components/PaymentMethods.vue'
// import SntTabs from '@/components/sale-page/components/Tabs.vue'
// import SntPromoCodes from '@/components/sale-page/components/PromoCodes.vue'
// import SntNumberField from '@/components/sale-page/components/NumberField.vue'
// import SntCollapse from '@/components/sale-page/components/Collapse.vue'
// import SntCheckbox from '@/components/sale-page/components/Checkbox.vue'
// import SntPagination from '@/components/sale-page/components/Pagination.vue'
// import SntLogin from '@/components/sale-page/components/Login.vue'

import StickyBottom from '@/components/sale-page/directives/StickyBottom'

function resolveScopedSlots (vnode, key = '') {
  let scopedSlots
  if (vnode.componentOptions?.tag === 'snt-app' && vnode?.data?.scopedSlots?.default) {
    scopedSlots = vnode.data.scopedSlots.default(
      vnode.context?.$shipnity || {}
    )
    vnode.componentOptions.children = scopedSlots
    vnode.data.scopedSlots = {}
  }
  const children = vnode?.children
    || vnode.componentOptions?.children
    || []
  children.forEach((cVnode, i) => {
    let cKey = `children[${i}]`
    if (vnode.componentOptions?.children) {
      cKey = `componentOptions.children[${i}]`
    }
    const newKey = _.join([key, cKey].filter((str) => str), '.')
    resolveScopedSlots(cVnode, newKey)
  })
  return vnode
}

function getTags (vnode, key = '') {
  const children = vnode?.children
    || vnode.componentOptions?.children

  return (children?.map((el, i) => {
    let cKey = `children[${i}]`
    if (vnode.componentOptions?.children) {
      cKey = `componentOptions.children[${i}]`
    }
    const newKey = _.join([key, cKey].filter((str) => str), '.')
    const tags = getTags(el, newKey)
    const tag = el.componentOptions?.tag || el.tag
    const data = {
      tag,
      propsData: el.componentOptions?.propsData,
      children: tags,
      key: newKey
    }
    return data
  }) || [])
  .filter((el) => el.tag !== undefined)
}

function getTagsDeep (tags) {
  return _.flattenDeep(tags.map((tag) => [tag, getTagsDeep(tag.children)]))
}

function getComponents (tags, whitelist) {
  return getTagsDeep(tags)
    .filter((el) => whitelist.includes(el.tag))
}

function getSections (tags, sectionWhitelist, componentWhitelist) {
  let sections = []
  tags.forEach((el) => {
    if (sectionWhitelist.includes(el.tag)) {
      el.children = getComponents(el.children, componentWhitelist)
      sections.push(el)
    } else {
      sections = getSections(el.children, sectionWhitelist, componentWhitelist)
    }
  })
  return sections
}

function compile () {
  const xtemplate = this
  const compiledTemplate = Vue.compile(xtemplate.template)
  const vm = new Vue({
    // components: xtemplate.components,
    directives: xtemplate.directives,
    data () {
      return xtemplate.data
    },
    render: compiledTemplate.render,
    staticRenderFns: compiledTemplate.staticRenderFns
  })
  const x = Vue.compile(xtemplate.template)
  const renderer = x.render.call(vm)
  return renderer
  // return resolveScopedSlots(renderer)
}

const sectionWhitelist = ['snt-section']
const componentWhitelist = [
  'snt-text',
  'snt-text-editor',
  'snt-navbar',
  'snt-product',
  'snt-products',
  'snt-all-products',
  'snt-product-detail',
  'snt-video',
  'snt-image',
  'snt-footer',
  'snt-feed',
  'snt-feeds',
  'snt-carousel',
  'snt-link',
  'snt-icon',
  'snt-cart',
  'snt-payment',
  'snt-order',
  'snt-orders',
  'snt-card',
  'snt-dialog'
]
function compute () {
  const xtemplate = this
  const compiled = xtemplate.compile()
  const tags = getTags(compiled)
  const sections = getSections(tags, sectionWhitelist, componentWhitelist)
  const keys = []
  const values = []
  sections.forEach((section, i) => {
    keys.push({
      key: section.key,
      name: section.propsData.name,
      values: section.children.map((component) => ({
        key: component.key,
        component: component.tag
      }))
    })
    values.push({
      sequence: i,
      hidden: false,
      values: section.children.map((component) => ({
        component: component.tag,
        props: component.propsData
      }))
    })
  })
  return {
    vnode: compiled,
    keys,
    values
  }
}

function combine (keyValues) {
  const xtemplate = this
  let compiled = xtemplate.compile()
  keyValues.forEach((section, i) => {
    compiled = _.update(
      compiled,
      `${section.key}.data.attrs`,
      (props) => ({
        ...props,
        'data-index': i,
        'data-sequence': section.sequence,
        'hidden': section.hidden
      })
    )
    section.values.forEach((component, j) => {
      compiled = _.update(
        compiled,
        `${component.key}.data.attrs`,
        (props) => ({
          ...props,
          'data-index': j
        })
      )
      compiled = _.update(
        compiled,
        `${component.key}.componentOptions`,
        (props) => _.assignIn(props, {
          propsData: component.props
        })
      )
    })
  })
  return compiled
}

function build (template, values) {
  const wrapper = document.createElement('div')
  wrapper.innerHTML = template
  const sectionWhitelistStr = this.$_.join(sectionWhitelist, ',')
  const sections = wrapper.getElementsByTagName(sectionWhitelistStr)
  const componentWhitelistStr = this.$_.join(componentWhitelist, ',')
  values.forEach((section, i) => {
    const components = sections[i].querySelectorAll(componentWhitelistStr)
    section.values.forEach(({ props }, j) => {
      this.$_.forEach(props, (value, key) => {
        if (this.$_.isObject(value)) {
          components[j].setAttribute(`:${key}`, JSON.stringify(value))
        } else {
          components[j].setAttribute(key, value)
        }
      })
    })
  })
  return wrapper.innerHTML
}

function VueXTemplate ({ template = '', components = {}, data = {} }) {
  if (!(this instanceof VueXTemplate)) {
    return new VueXTemplate({ template, components, data })
  }

  const xtemplate = this
  xtemplate.template = `<div>${template}</div>`
  xtemplate.directives = {
    StickyBottom
  }
  // xtemplate.components = {
  //   SntApp,
  //   SntContainer,
  //   SntRow,
  //   SntCol,
  //   SntSection,
  //   SntNavbar,
  //   SntAllProducts,
  //   SntProducts,
  //   SntCart,
  //   SntPayment,
  //   SntText,
  //   SntIcon,
  //   SntLink,
  //   SntTextEditor,
  //   SntProductDetail,
  //   SntVideo,
  //   SntImage,
  //   SntFeeds,
  //   SntFeed,
  //   SntCarousel,
  //   SntFooter,
  //   SntOrderDetail,
  //   SntOrders,
  //   SntCheckout,
  //   SntCard,
  //   SntTooltip,
  //   SntDialog,
  //   SntButton,
  //   SntHover,
  //   SntTextField,
  //   SntSelect,
  //   SntShipping,
  //   SntAddress,
  //   SntPaymentMethods,
  //   SntTabs,
  //   SntPromoCodes,
  //   SntNumberField,
  //   SntCollapse,
  //   SntCheckbox,
  //   SntPagination,
  //   SntLogin,
  //   ...components
  // }
  xtemplate.data = data
}

VueXTemplate.prototype = { compile, compute, combine, build }
VueXTemplate.getTags = getTags
VueXTemplate.getTagsDeep = getTagsDeep
VueXTemplate.getComponents = getComponents
VueXTemplate.getSections = getSections
VueXTemplate.resolveScopedSlots = resolveScopedSlots
VueXTemplate.sectionWhitelist = sectionWhitelist
VueXTemplate.componentWhitelist = componentWhitelist

export default VueXTemplate
