<script setup lang="ts">
import { ref, provide, watch, onMounted } from 'vue'
import { apiRequest } from '@/core/api'
import { ClientAblyService } from '@/core/AblyService'
import { useLocalTranslation } from '@/lib/composables/useLocalTranslation'
import { useActionHandler } from '@/lib/composables/useActionHandler'
import { getPrimaryColorStyles } from '@/lib/theme-utils'
import { defaultLanguage } from '@/i18n'
import type { HelpScreenData, Message, Option, ReviewProps } from '@/lib/types'
import FloatingMessage from './FloatingMessage.vue'
import HelpButton from './HelpButton.vue'
import HelpPopup from './HelpPopup.vue'
import '@/globals.css'

const props = withDefaults(defineProps<{
  helpScreenId: string
  language?: 'ar' | 'en'
  user?: { id?: string; name?: string; email?: string; avatar?: string }
  showArrow?: boolean
  messageLabel?: string | null
  primaryColor?: string
}>(), {
  language: defaultLanguage,
  showArrow: true,
  messageLabel: null,
})

provide('babylai-language', props.language)

const { t } = useLocalTranslation()
const actionHandler = useActionHandler()

const isOpen = ref(false)
const isClosing = ref(false)
const showArrowAnimation = ref(props.showArrow)
const helpScreenData = ref<HelpScreenData | null>(null)
const status = ref('idle')
const error = ref<string | null>(null)
const selectedOption = ref<Option | null>(null)
const sessionId = ref<string | null>(null)
const reviewSessionId = ref<string | null>(null)
const inChatReviewSessionId = ref<string | null>(null)
const isAblyConnected = ref(false)
const isChatClosed = ref(false)
const messages = ref<Message[]>([])
const needsAgent = ref(false)
const assistantStatus = ref('idle')
const isReviewDialogOpen = ref(false)
const isSubmittingReview = ref(false)
const navigateToOptionsListAfterReview = ref(false)

function handleTogglePopup() {
  if (isOpen.value) {
    isClosing.value = true
    isOpen.value = false
    showArrowAnimation.value = true
  } else {
    isOpen.value = true
    showArrowAnimation.value = false
  }
}

function handleClosePopup() {
  isClosing.value = true
  isOpen.value = false
}

function handlePopupCloseAnimationEnd() {
  isClosing.value = false
}

function handleCloseArrowAnimation() {
  showArrowAnimation.value = false
}

function handleReceiveMessage(
  message: string,
  senderType: number,
  receivedNeedsAgent: boolean,
  attachmentUrls: string[] = [],
  attachmentIds: string[] = []
) {
  if (receivedNeedsAgent) needsAgent.value = true

  const newMessage: Message = {
    id: Date.now(),
    senderType,
    messageContent: message,
    sentAt: new Date(),
    isSeen: true,
    ...(attachmentUrls.length > 0 && { attachmentUrls }),
    ...(attachmentIds.length > 0 && { attachmentIds }),
  }
  messages.value = [...messages.value, newMessage]
  assistantStatus.value = 'idle'
}

async function handleEndChat(options?: { fromChatScreen?: boolean; sessionClosedByServer?: boolean }) {
  if (!sessionId.value || !selectedOption.value) return

  const fromChatScreen = options?.fromChatScreen === true
  const sessionClosedByServer = options?.sessionClosedByServer === true
  const closingSessionId = sessionId.value

  try {
    await ClientAblyService.stopConnection()
    isAblyConnected.value = false
    assistantStatus.value = 'idle'

    if (!sessionClosedByServer) {
      const response = await apiRequest(`Client/ClientChatSession/${closingSessionId}/close`, 'POST', null, {
        language: props.language,
      })
      if (!response.ok) throw new Error('Failed to close chat session')
    }

    reviewSessionId.value = closingSessionId
    sessionId.value = null

    if (fromChatScreen) {
      inChatReviewSessionId.value = closingSessionId
      isChatClosed.value = true
    } else {
      selectedOption.value = null
      messages.value = []
      isReviewDialogOpen.value = true
    }
  } catch {
    error.value = 'Failed to end chat session'
    assistantStatus.value = 'idle'
    reviewSessionId.value = closingSessionId
    sessionId.value = null
    if (!fromChatScreen) selectedOption.value = null
  }
}

async function handleSendChatReview({ comment, rating }: ReviewProps) {
  if (!reviewSessionId.value) return

  const wasInChatReview = !!inChatReviewSessionId.value
  isSubmittingReview.value = true
  try {
    const response = await apiRequest(
      `Client/ClientChatSession/${reviewSessionId.value}/review`,
      'POST',
      { rating, comment },
      { language: props.language }
    )
    if (!response.ok) throw new Error('Failed to send chat review')

    handleCloseChatReview()
    if (wasInChatReview) navigateToOptionsListAfterReview.value = true
  } catch {
    error.value = 'Failed to send chat review'
  } finally {
    isSubmittingReview.value = false
  }
}

function handleCloseChatReview() {
  isReviewDialogOpen.value = false
  reviewSessionId.value = null
  inChatReviewSessionId.value = null
}

function handleStartChat(option: Option) {
  inChatReviewSessionId.value = null
  messages.value = [
    {
      id: Date.now(),
      senderType: 3,
      messageContent: option?.assistant?.greeting || 'مرحباً! كيف يمكنني مساعدتك اليوم؟',
      sentAt: new Date(),
      isSeen: true,
    },
  ]
  isChatClosed.value = false
  status.value = 'succeeded'
}

async function startNewChatSession(option: Option): Promise<string> {
  status.value = 'loading'
  error.value = null

  const chatSessionCreateDto = {
    helpScreenId: props.helpScreenId,
    optionId: option.id,
    ...(props.user && {
      user: { id: props.user.id, name: props.user.name, email: props.user.email },
    }),
  }

  const response = await apiRequest('Client/ClientChatSession/create-session', 'POST', chatSessionCreateDto, {
    language: props.language,
  })

  if (!response.ok) throw new Error('Failed to create chat session')

  const responseData = await response.json()
  const newSessionId: string = responseData.chatSession.id

  sessionId.value = newSessionId

  await ClientAblyService.startConnection(
    newSessionId,
    responseData.ablyToken,
    handleReceiveMessage,
    responseData.chatSession.tenantId,
    (actionType: string | undefined | null, messageData: unknown) => {
      void actionHandler.handleAction(actionType, messageData)
    }
  )

  if (!ClientAblyService.isConnectionActive()) throw new Error('Ably connection failed to establish properly')

  isAblyConnected.value = true
  isChatClosed.value = false
  status.value = 'succeeded'

  return newSessionId
}

async function handleEnsureSession(): Promise<string> {
  if (sessionId.value) return sessionId.value
  if (selectedOption.value) return startNewChatSession(selectedOption.value)
  throw new Error('No option selected')
}

async function handleSendMessage(message: string, attachmentIds: string[] = []) {
  if (message.trim() === '' && attachmentIds.length === 0) return

  try {
    assistantStatus.value = 'typing'

    const userMessage: Message = {
      id: Date.now(),
      senderType: 1,
      messageContent: message || '',
      sentAt: new Date(),
      isSeen: false,
      ...(attachmentIds.length > 0 && { attachmentIds }),
    }
    messages.value = [...messages.value, userMessage]

    let currentSessionId = sessionId.value

    if (!currentSessionId && !isAblyConnected.value && selectedOption.value) {
      currentSessionId = await startNewChatSession(selectedOption.value)
    }

    if (!currentSessionId) throw new Error('No active session available')

    const messageDto = {
      messageContent: message || '',
      ...(attachmentIds.length > 0 && { attachmentIds }),
    }

    const response = await apiRequest(
      `Client/ClientChatSession/${currentSessionId}/send-message`,
      'POST',
      messageDto,
      { language: props.language }
    )

    if (!response.ok) throw new Error('Failed to send message')

    messages.value = messages.value.map((msg) =>
      msg.senderType === 1 && !msg.isSeen ? { ...msg, isSeen: true } : msg
    )
  } catch {
    assistantStatus.value = 'idle'
    messages.value = [
      ...messages.value,
      {
        id: Date.now(),
        senderType: 3,
        messageContent: 'Failed to send the message. Please try again.\n لم يتم إرسال الرسالة. يرجى المحاولة مرة أخرى.',
        sentAt: new Date(),
        isSeen: true,
      },
    ]
  }
}

onMounted(() => {
  actionHandler.registerHandler('needs_agent', () => {
    needsAgent.value = true
  })
  actionHandler.registerHandler('end_session', () => {
    void handleEndChat({ fromChatScreen: true, sessionClosedByServer: true })
  })
})

watch(
  () => [isOpen.value, props.helpScreenId] as const,
  ([open, screenId]) => {
    if (open && screenId) {
      status.value = 'loading'
      apiRequest(`client/clientHelpScreen/${screenId}`, 'GET', null, { language: props.language })
        .then((res) => res.json())
        .then((data) => {
          helpScreenData.value = data
          status.value = 'succeeded'
          error.value = null
        })
        .catch((err: Error) => {
          error.value = err.message
          status.value = 'failed'
        })
    }
  }
)

const themeStyles = getPrimaryColorStyles(props.primaryColor)
</script>

<template>
  <div class="babylai-theme-root babylai:mb-4" :style="themeStyles">
    <FloatingMessage
      v-if="showArrowAnimation && !isOpen && !isClosing"
      :message="messageLabel || t('homeSdk.needAssistance')"
      @close="handleCloseArrowAnimation"
    />

    <HelpButton @click="handleTogglePopup" />

    <HelpPopup
      v-if="isOpen || isClosing"
      :is-open="isOpen"
      :is-closing="isClosing"
      :help-screen="helpScreenData"
      :status="status"
      :error="error"
      :messages="messages"
      :assistant-status="assistantStatus"
      :needs-agent="needsAgent"
      :session-id="sessionId"
      :is-chat-closed="isChatClosed"
      :selected-option="selectedOption"
      :in-chat-review-session-id="inChatReviewSessionId"
      :is-submitting-review="isSubmittingReview"
      :navigate-to-options-list-after-review="navigateToOptionsListAfterReview"
      :is-review-dialog-open="isReviewDialogOpen"
      :review-session-id="reviewSessionId"
      :on-close="handleClosePopup"
      :on-close-animation-end="handlePopupCloseAnimationEnd"
      :on-start-chat="handleStartChat"
      :on-send-message="handleSendMessage"
      :on-ensure-session="handleEnsureSession"
      :on-end-chat="handleEndChat"
      :set-selected-option="(opt) => selectedOption = opt"
      :on-in-chat-review-submit="handleSendChatReview"
      :on-in-chat-review-done="handleCloseChatReview"
      :on-navigated-to-options-list="() => navigateToOptionsListAfterReview = false"
      :on-review-dialog-submit="handleSendChatReview"
      :on-review-dialog-close="handleCloseChatReview"
    />
  </div>
</template>
