import { Message } from '../atoms/message.js'
import * as matter from '../export-matter.js'
import h from '../shared/h.js'
import { isPublish } from '../shared/wcm-mode.js'
import { Empty } from './empty.js'
import { ErrorHandler } from './error-handler.js'

const AUTHOR_SNIPPET_ORGANISMS = {
  Notification: 'Notification'
}

// Track all component models by the Preact key.
export const componentModels = new Map()

function normalizeNestedColumns (componentModel, parentComponentModel) {
  if (parentComponentModel?.colspan) {
    // Make sure the child is capped.
    if (!componentModel.colspan) {
      componentModel.colspan = parentComponentModel.colspan
    }

    // Reset colstart if larger than available width of the container.
    if (componentModel.colstart && componentModel.colstart > parentComponentModel.colspan) {
      componentModel.colstart = 1
    }

    // Adjust colspan if colstart + colspan does not fit in the container.
    const colstart = componentModel.colstart || 1
    if (componentModel.colspan + colstart + 1 > parentComponentModel.colspan) {
      componentModel.colspan = parentComponentModel.colspan - colstart + 1
    }
  }
}

function getChildren (componentModel, trail) {
  // Container handling.
  const children = []
  const _wrap = []

  const childrenToIterate = componentModel?.children ?? []
  for (const [ii, childComponentModel] of childrenToIterate.entries()) {
    normalizeNestedColumns(childComponentModel, componentModel)

    // Get wrapper configuration for container.
    const { colstart, colspan } = childComponentModel
    _wrap.push({ colstart, colspan })

    // Get children
    children.push(h(Component, {
      _componentModel: childComponentModel,
      _model: childComponentModel.model,
      _parentColspan: colspan,
      _trail: trail && [...trail, ii]
    }))
  }

  return { _wrap, children }
}

function resolveOrganism (_componentModel) {
  let [organismName, variantName] = (_componentModel?.organism ?? '').split('.')
  variantName = variantName?.toLowerCase()

  let Organism = matter[organismName]
  if (!Organism) {
    Organism = Empty
    organismName = 'Empty'
    console.log(`Unable to resolve organism: ${_componentModel.id}`)
  }

  return { Organism, organismName, variantName }
}

function fastHash (str) {
  let hash = 0
  for (let ii = 0; ii < str.length; ++ii) {
    hash = (hash << 5) - hash + str.codePointAt(ii)
  }
  return hash >>> 0
}

function getKey (componentModel, trail) {
  // Return trail for containers.
  if (trail && componentModel.children) {
    return trail.join('/')
  }

  // Return hash for web components
  if (componentModel.organism === 'WebComponent') {
    return fastHash(JSON.stringify(componentModel.model))
  }

  // Do not use keys for regular components.
  return componentModel.id
}

export function Component ({ _componentModel, _parentColspan, _trail = ['0'] }) {
  const { Organism, organismName, variantName } = resolveOrganism(_componentModel)

  // Clear component models for each new page.
  if (_componentModel?.organism === 'Page') {
    componentModels.clear()
  }

  // Reset trail for main container (and children)
  if (_componentModel?.organism === 'Container.Main') {
    _trail = null
  }

  // Get key and store component model.
  const key = getKey(_componentModel, _trail)
  componentModels.set(key, _componentModel)

  // Get organism properties.
  const organismProps = {
    key,
    _color: _componentModel.color,
    _model: _componentModel.model,
    _variant: variantName,
    _parentColspan,
    'data-key': key,
    'data-name': organismName,
    id: _componentModel.id
  }

  if (!isPublish && Object.keys(AUTHOR_SNIPPET_ORGANISMS).includes(organismName)) {
    // Show author snippet for selected components.
    return h(Message, AUTHOR_SNIPPET_ORGANISMS[organismName])
  }

  // Resolve children and wrap configuration.
  const { _wrap, children } = getChildren(_componentModel, _trail)

  return h(ErrorHandler,
    h(Organism, { ...organismProps, _wrap }, children)
  )
}
