<template>
  <div class="w-full md:min-w-[350px] max-w-[750px] flex flex-col absolute left-1/2 -translate-x-1/2 bottom-5">
    <div ref="chatWindow" class="w-full overflow-auto h-32 mb-5 bg-gray rounded-md border-2 border-solid border-gray-500 text-gray-300" v-show="gameStore.uiSettings.isChatOpen">
      <div v-for="message in chats" class="flex-col py-2 items-center p-3">
        <span class="text-ellipsis overflow-hidden whitespace-nowrap text-sm" :class="{ 'text-cyan-300': gameStore.character?.role == 'gm' }">{{ message.character.name }}</span>
        <p class="text-gray-50 m-0">{{ message.message }}</p>
      </div>
    </div>
    <div class="w-96 mx-auto relative">
      <img src="/assets/icons/ingameUI/chat-icon.svg" class="absolute top-1/2 -translate-y-1/2 left-2.5 h-4 w-4 opacity-50" alt="" />
      <input
        class="w-[332px] h-8 rounded-sm text-xs font-default pl-8 pr-4 py-0 bg-gray-600 border-2 border-solid border-gray-500 text-gray-300 bg-[url('/assets/ui-texture.png')] bg-no-repeat bg-cover focus:outline-none focus:ring-0 focus:border-cyan-800"
        placeholder="Type something..."
        v-model="message"
        @keypress="handleKeyPress"
        @submit="handleSubmit"
        ref="chatInput"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import type { Chat } from '@/application/types'
import { useGameStore } from '@/stores/gameStore'
import { useMapStore } from '@/stores/mapStore'
import { onClickOutside, useFocus } from '@vueuse/core'
import { useScene } from 'phavuer'
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import { SocketEvent } from '@/application/enums'

const scene = useScene()
const gameStore = useGameStore()
const mapStore = useMapStore()

const message = ref('')
const chats = ref([] as Chat[])
const chatWindow = ref<HTMLElement | null>(null)
const chatInput = ref<HTMLElement | null>(null)

const { focused } = useFocus(chatInput)

function focusChat(event: KeyboardEvent) {
  if (event.key === 'Enter' && !focused.value) {
    focused.value = true
  }
}

onClickOutside(chatInput, (event) => unfocusChat(event, chatInput.value as HTMLElement))

function unfocusChat(event: Event, targetElement: HTMLElement) {
  if (!(event.target instanceof Node) || !targetElement.contains(event.target)) {
    targetElement.blur()
  }
}

const sendMessage = () => {
  if (!message.value.trim()) return
  gameStore.connection?.emit('chat:message', { message: message.value }, (response: boolean) => {})
  message.value = ''
}

const handleSubmit = (event: Event) => {
  event.preventDefault()
  sendMessage()
}

const handleKeyPress = (event: KeyboardEvent) => {
  if (event.key === 'Enter' && !event.shiftKey) {
    event.preventDefault()
    sendMessage()
  }
}

const scrollToBottom = () => {
  nextTick(() => {
    if (chatWindow.value) {
      chatWindow.value.scrollTop = chatWindow.value.scrollHeight
    }
  })
}

gameStore.connection?.on(SocketEvent.CHAT_MESSAGE, (data: Chat) => {
  chats.value.push(data)
  scrollToBottom()

  if (!mapStore.characterLoaded) return

  const characterContainer = scene.children.getByName(data.character.name) as Phaser.GameObjects.Container
  if (!characterContainer) return

  const characterChatContainer = characterContainer.getByName(data.character.name + '_chatContainer') as Phaser.GameObjects.Container
  if (!characterChatContainer) return

  const chatBubble = characterChatContainer.getByName(data.character.name + '_chatBubble') as Phaser.GameObjects.Container
  const chatText = characterChatContainer.getByName(data.character.name + '_chatText') as Phaser.GameObjects.Text
  if (!chatText || !chatBubble) return

  function calculateTextWidth(text: string, font: string, fontSize: number): number {
    // Create a canvas element
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d')

    if (!context) {
      throw new Error('Unable to create canvas context')
    }

    // Set the font
    context.font = `${fontSize}px ${font}`

    // Measure the text width
    const metrics = context.measureText(text)

    return metrics.width
  }

  chatBubble.width = calculateTextWidth(data.message.substring(0, 90), 'Arial', 13) + 30

  // setText but with max. char limit of 90
  chatText.setText(data.message.substring(0, 90))

  characterChatContainer.setVisible(true)

  /**
   * Hide chat bubble after a few seconds
   */

  // Clear any existing hide timer
  if (characterChatContainer.getData('hideTimer')) {
    clearTimeout(characterChatContainer.getData('hideTimer'))
  }

  // Set a new hide timer
  const hideTimer = setTimeout(() => {
    characterChatContainer.setVisible(false)
  }, 3000)

  // Store the timer on the container itself
  characterChatContainer.setData('hideTimer', hideTimer)
})
scrollToBottom()

onMounted(() => {
  addEventListener('keydown', focusChat)
})

onBeforeUnmount(() => {
  gameStore.connection?.off(SocketEvent.CHAT_MESSAGE)
  removeEventListener('keydown', focusChat)
})
</script>
