import ICON_ARROW from './resources/arrow-next.svg'
import ICON_CLOSE from './resources/navigation-close.svg'
import ICON_PERSON from './resources/person.svg'
import ICON_UPLOAD from './resources/upload.svg'

const { h, matter, preactHooks } = globalThis.ami
const { useEffect, useLayoutEffect, useRef, useState } = preactHooks
const { Button, Icon, Input } = matter

const IS_PUBLISH = document.documentElement.dataset.mode === 'publish'

export default function ChatBot ({
  closeText = 'Close window',
  buttonText = 'Ask us',
  headerText = 'Chatbot',
  sendText = 'Send',
  attachText = 'Attach',
  inputText = 'Type your message',
  icon,
  messages,
  forceOpen,
  isBusy,
  onInit,
  onSend,
  onAttach,
  onClose
}) {
  // Show only on publish.
  if (!IS_PUBLISH) {
    return null
  }

  const [isVisible, setVisible] = useState(forceOpen)
  const [isInitialized, setInitialized] = useState(false)
  const [prevBusy, setPrevBusy] = useState(isBusy)
  const ref = useRef()

  // Trigger close on before unload.
  useEffect(() => {
    window.addEventListener('beforeunload', onClose)
    return () => window.removeEventListener('beforeunload', onClose)
  }, [])

  // Set focus when recovering from busy state.
  useLayoutEffect(() => {
    if (!isBusy && prevBusy) {
      ref.current?.base?.focus?.()
    }

    if (isBusy !== prevBusy) {
      setPrevBusy(isBusy)
    }
  }, [isBusy, prevBusy])

  // Trigger onInit event.
  if (isVisible && !isInitialized) {
    onInit?.()
    setInitialized(true)
  }

  // Chat button
  if (!isVisible) {
    return h('div', { className: 'fixed end-20 bottom-6 z-20 cursor-pointer shadow rounded-xl animate-fade print:hidden' },
      h(Button, {
        _size: 'sm',
        _rtlFlip: true,
        _model: { name: buttonText, icon },
        onClick () {
          setVisible(true)
        }
      })
    )
  }

  // Chat window.
  return [
    h('div', {
      // Z-index must be below 30 (popups) and above 20 (floating elements).
      _style: id => `
        ${id} { z-index: 21 }
        @media screen and (min-width: 960px) {
          ${id} { width: 500px; height: 700px; max-height: calc(100vh - 48px) }
        }
      `,
      className: 'shadow-lg fixed end-20 bottom-6 bg-bga text-txa border-txa/20 flex flex-col animate-toast lg:rounded-xl sm:mr-0 md:mr-0 sm:inset-0 md:inset-0',
      children: [
        h('div', { className: 'text-txb bg-bgb flex lg:rounded-tl-xl lg:rounded-tr-xl' },
          h('div', { className: 'flex grow font-bold justify-center items-center ms-10' },
            headerText
          ),
          h('button', {
            className: 'p-2 grow-0 cursor-pointer',
            children: h(Icon, ICON_CLOSE),
            title: closeText,
            onClick () {
              setVisible(false)
            }
          })
        ),
        h(Messages, { messages }),
        h('form', {
          className: 'p-2 flex space-x-2',
          children: [
            onAttach && h('div', { className: 'shrink-0' },
              AttachButton({ attachText, onAttach, isBusy })
            ),
            h(Input, {
              ref,
              disabled: isBusy,
              placeholder: inputText,
              className: 'grow',
            }),
            h('div', { className: 'shrink-0' },
              h(Button, {
                disabled: isBusy,
                type: 'submit',
                onClick (event) {
                  event.preventDefault()
                  const input = event.currentTarget.parentElement.previousElementSibling
                  const text = input.value.trim()
                  if (text) {
                    onSend?.(text)
                    input.value = ''
                  }
                },
                _textless: true,
                _model: { name: sendText, icon: ICON_ARROW }
              })
            )
          ]
        })
      ]
    })
  ]
}

function Messages ({ messages }) {
  const ref = useRef(null)

  // Scroll to the last message.
  // ResizeObserver is necessary due to the unknow size of images.
  useEffect(() => {
    const observer = new globalThis.ResizeObserver(entries => {
      for (const entry of entries) {
        const parent = entry.target.parentElement
        parent.scrollTop = parent.scrollHeight
      }
    })

    observer.observe(ref.current)
  }, [])

  // Render messages.
  const locale = document.documentElement.lang || navigator.language

  return h('div', { role: 'log', className: 'grow overflow-y-auto' },
    h('div', { ref, className: 'space-y-2 pl-2 pr-2 pt-2' },
      messages.map(m =>
        h('div',
          // Timestamp.
          m.timestamp && h('time', {
            className: m.isBot ? 'ml-8 flex text-sm' : 'flex justify-end text-sm',
            children: m.timestamp.toLocaleString(locale),
          }),

          // Message.
          h('div', { className: m.isBot ? 'flex' : 'flex justify-end' },
            // Person icon or space.
            h('div', { className: 'flex items-end w-8 shrink-0 grow-0' },
              m.isBot && h(Icon, ICON_PERSON)
            ),

            // Bubble.
            h('div', {
              style: 'max-width: calc(100% - 32px); overflow-wrap: break-word',
              className: m.isBot
                ? 'rounded-tl-lg rounded-tr-lg rounded-br-lg inline-block p-2 border-txa/20 border space-y-2'
                : 'rounded-tl-lg rounded-tr-lg rounded-bl-lg inline-block p-2 text-txb bg-bgb',
              children: m.children
            })
          )
        )
      )
    )
  )
}

function AttachButton ({ attachText, onAttach, isBusy }) {
  const ref = useRef(null)

  useEffect(() => {
    const onChange = event => {
      const file = event.target.files[0]
      file && onAttach(file)
      event.target.value = '' // Clear after use. Otherwise reattaching the same file won't work.
    }
    ref.current?.addEventListener?.('change', onChange)

    return () => {
      ref.current?.removeEventListener?.('change', onChange)
    }
  }, [])

  return [
    h('input', { type: 'file', className: 'hidden', ref }),
    h(Button, {
      disabled: isBusy,
      _textless: true,
      _model: { name: attachText, icon: ICON_UPLOAD },
      type: 'button',
      onClick () {
        ref?.current?.click?.()
      }
    })
  ]
}
