import renderTemplateString from "../state/renderTemplateString"
import appStore from "../state/store"
import hasBinding from "../state/hasBinding"

export const prepareRenderComponent = ({
  ComponentConstructor,
  htmlElement,
  anchor,
  props,
  parentNode,
  contextStoreKey,
}) => {
  const thisNode = createTreeNode()
  thisNode.parentNode = parentNode
  thisNode.props = props
  thisNode.contextStoreKey = contextStoreKey

  // the treeNode is first created (above), and then this
  // render method is add. The treeNode is returned, and
  // render is called later (in attachChildren)
  thisNode.render = initialProps => {
    thisNode.component = new ComponentConstructor({
      target: htmlElement,
      props: initialProps,
      hydrate: false,
      anchor,
    })

    // finds the root element of the component, which was created by the contructor above
    // we use this later to attach a className to. This is how styles
    // are applied by the builder
    thisNode.rootElement = htmlElement.children[htmlElement.children.length - 1]

    let [componentName] = props._component.match(/[a-z]*$/)
    if (props._id && thisNode.rootElement) {
      thisNode.rootElement.classList.add(`${componentName}-${props._id}`)
    }

    // make this node listen to the store
    if (thisNode.stateBound) {
      const unsubscribe = appStore.subscribe(state => {
        const storeBoundProps = Object.keys(initialProps._bb.props).filter(p =>
          hasBinding(initialProps._bb.props[p])
        )
        if (storeBoundProps.length > 0) {
          const toSet = {}
          for (let prop of storeBoundProps) {
            const propValue = initialProps._bb.props[prop]
            toSet[prop] = renderTemplateString(propValue, state)
          }
          thisNode.component.$set(toSet)
        }
      }, thisNode.contextStoreKey)
      thisNode.unsubscribe = unsubscribe
    }
  }

  return thisNode
}

export const createTreeNode = () => ({
  context: {},
  props: {},
  rootElement: null,
  parentNode: null,
  children: [],
  bindings: [],
  component: null,
  unsubscribe: () => {},
  render: () => {},
  get destroy() {
    const node = this
    return () => {
      if (node.children) {
        // destroy children first - from leaf nodes up
        for (let child of node.children) {
          child.destroy()
        }
      }
      if (node.unsubscribe) node.unsubscribe()
      if (node.component && node.component.$destroy) node.component.$destroy()
      for (let onDestroyItem of node.onDestroy) {
        onDestroyItem()
      }
    }
  },
  onDestroy: [],
})
