/* eslint-disable no-param-reassign */
import _ from 'lodash'
import Vue from 'vue'
import router from '../router'
import ShipnityFramework from '../framework'

export function resolveScopedSlots (vnode, key = '') {
  if (vnode.componentOptions?.tag === 'snt-app' && vnode?.data?.scopedSlots?.default) {
    vnode.componentOptions.children = vnode.data.scopedSlots.default({
      _: Vue.prototype.$_,
      router: vnode.context.$router,
      route: vnode.context.$route,
      shipnity: vnode.context.$shipnity,
      loading: false,
      subdomain: null,
      isMobileDevice: false,
      isAuth: false,
      customer: '',
      theme: vnode.context.$shipnity.theme,
      productBrands: vnode.$data.productBrands || [],
      productCategories: [{ id: 1, name: 'เสื้อผ้า' }, { id: 2, name: 'กางเกง' }, { id: 3, name: 'รองเท้า' }],
      purchasesQuantity: 0,
      breakpoint: vnode.context.$shipnity.breakpoint,
      notification: vnode.context.$shipnity.notification,
      scrollY: 0,
      scrollUp: 0,
      customerName: null,
      goTo: () => {},
      addToCart: () => {},
      login: () => {}
    })
    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
}
export 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)
}
export function getTagsDeep (tags) {
  return _.flattenDeep(tags.map((tag) => [tag, getTagsDeep(tag.children)]))
}
export function getComponents (tags, whitelist) {
  return getTagsDeep(tags)
    .filter((el) => whitelist.includes(el.tag))
}
export 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
}

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 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,
    router,
    shipnity: new ShipnityFramework({}).framework
  })
  const x = Vue.compile(xtemplate.template)
  return x.render.call(vm)
}

function compute () {
  const xtemplate = this
  const compiled = resolveScopedSlots(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 = resolveScopedSlots(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 (values) {
  // const x = `<snt-carousel
  //       v-if="breakpoint.xs"
  //       :items="item.images.map(src => ({src}))"
  //       class="product-detail__carousel-images"
  //       height="400"
  //       hide-delimiter-background
  //       show-arrows-on-hover
  //       @change="selectImage($event)" />`
  // console.log(x.replace(/<([^>]*)\/>/g, '<$1></$1>'))
  const xtemplate = this
  const html = xtemplate.template.replace(/<([^>]*)\/>/g, '<$1></$1>')
  const dom = new DOMParser().parseFromString(html, 'text/html')
  const sectionWhitelistStr = _.join(sectionWhitelist, ',')
  const sections = dom.getElementsByTagName(sectionWhitelistStr)
  const componentWhitelistStr = _.join(componentWhitelist, ',')
  values.forEach((section, i) => {
    sections[i].setAttribute('data-index', Number(i))
    sections[i].setAttribute('data-sequence', Number(section.sequence))
    sections[i].setAttribute('data-hidden', section.hidden)
    const components = sections[i].querySelectorAll(componentWhitelistStr)
    section.values.forEach(({ props }, j) => {
      components[j].setAttribute('data-index', Number(j))
      _.forEach(props, (value, key) => {
        const propName = _.kebabCase(key)
        components[j].removeAttribute(`:${propName}`)
        if (_.isObject(value)) {
          components[j].setAttribute(`:${propName}`, JSON.stringify(value))
        } else if (_.isBoolean(value) || _.isNumber(value)) {
          components[j].setAttribute(`:${propName}`, value)
        } else {
          components[j].setAttribute(propName, value)
        }
      })
    })
  })
  xtemplate.template = dom.body.innerHTML
  return xtemplate.compile()
}

export function VueXTemplate ({ template = '', components = {}, directives = {}, data = {} }) {
  if (!(this instanceof VueXTemplate)) {
    return new VueXTemplate({ template, components, data })
  }
  const xtemplate = this
  xtemplate.template = `<div>${template}</div>`
  xtemplate.directives = {
    ...directives
  }
  xtemplate.components = {
    ...components
  }
  xtemplate.data = {
    ...data
  }
}
VueXTemplate.prototype = { compile, compute, combine, build }
