<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
import { useApi } from '../composables/useApi'
import { useChatStream } from '../composables/useChatStream'
import {
  FAST_TRANSITION_DURATION_MS,
  MESSAGE_ENTRANCE_ANIMATION_DURATION_MS,
  PLAYLIST_SAVED_ANIMATION_DURATION_MS,
  REDUCED_MOTION_DURATION_MS,
  THINKING_PULSE_ANIMATION_DURATION_MS,
  THINKING_PULSE_STAGGER_MS,
  TRACK_CARD_ANIMATION_DURATION_MS,
} from '../lib/constants'
import { formatMessage, messages } from '../lib/messages'
import { DISCOVERY_SUGGESTIONS } from '../lib/suggestions'
import AppHeader from './AppHeader.vue'
import DevPanel from './DevPanel.vue'
import MessageInput from './MessageInput.vue'
import MessageList from './MessageList.vue'
import ModeBanner from './ModeBanner.vue'

const draft = ref('')
const devOpen = ref(false)
const devOpener = ref<HTMLElement | null>(null)
const input = ref<InstanceType<typeof MessageInput> | null>(null)
const { auth, health, bootstrap, logout, createPlaylist } = useApi()
const { turns, eventLog, isStreaming, turnCount, latestAssistant, send, savePlaylist, reset } =
  useChatStream(createPlaylist)

const timingStyles: Record<string, string> = {
  '--dur-fast': `${FAST_TRANSITION_DURATION_MS}ms`,
  '--dur-card': `${TRACK_CARD_ANIMATION_DURATION_MS}ms`,
  '--dur-message': `${MESSAGE_ENTRANCE_ANIMATION_DURATION_MS}ms`,
  '--dur-playlist-saved': `${PLAYLIST_SAVED_ANIMATION_DURATION_MS}ms`,
  '--dur-thinking-pulse': `${THINKING_PULSE_ANIMATION_DURATION_MS}ms`,
  '--delay-thinking-pulse': `${THINKING_PULSE_STAGGER_MS}ms`,
  '--dur-reduced-motion': `${REDUCED_MOTION_DURATION_MS}ms`,
}
const mode = computed(() => {
  if (health.value.status === 'ready') return health.value.mode
  return null
})
const canSave = computed(() => auth.value.status === 'authenticated')
const turnCounter = computed(() => {
  if (turnCount.value === 0) return messages.chatViewNoTurns
  if (turnCount.value === 1) {
    return formatMessage('chatViewOneTurn', { count: turnCount.value })
  }
  return formatMessage('chatViewManyTurns', { count: turnCount.value })
})

async function focusInput(): Promise<void> {
  await nextTick()
  input.value?.focus()
}

function pickSuggestion(suggestion: string): void {
  draft.value = suggestion
  void focusInput()
}

function retryQuery(query: string): void {
  draft.value = query
  void focusInput()
}

function submit(query: string): void {
  if (isStreaming.value) return
  draft.value = ''
  void send(query)
}

function openDevPanel(opener?: HTMLElement): void {
  if (opener) {
    devOpener.value = opener
    devOpen.value = true
    return
  }
  const activeElement = document.activeElement
  if (activeElement instanceof HTMLElement) {
    devOpener.value = activeElement
  } else {
    devOpener.value = null
  }
  devOpen.value = true
}

function onShortcut(event: KeyboardEvent): void {
  if (event.ctrlKey && event.key.toLowerCase() === 'd') {
    event.preventDefault()
    if (devOpen.value) {
      devOpen.value = false
    } else {
      openDevPanel()
    }
  }
}

onMounted(() => {
  void bootstrap()
  window.addEventListener('keydown', onShortcut)
})

onUnmounted(() => window.removeEventListener('keydown', onShortcut))
</script>

<template>
  <div class="chat-view" :style="timingStyles">
    <div class="shell" :inert="devOpen">
      <AppHeader
        :auth="auth"
        :mode="mode"
        :health-failed="health.status === 'failed'"
        @logout="logout"
        @reset="reset"
        @toggle-dev="openDevPanel"
      />

      <ModeBanner v-if="mode === 'demo'" />

      <MessageList
        :turns="turns"
        :suggestions="DISCOVERY_SUGGESTIONS"
        :can-save="canSave"
        @pick="pickSuggestion"
        @save="savePlaylist"
        @retry="retryQuery"
      />

      <MessageInput
        ref="input"
        v-model="draft"
        :disabled="isStreaming"
        :turn-counter="turnCounter"
        @send="submit"
      />
    </div>

    <DevPanel
      v-if="devOpen"
      :latest="latestAssistant"
      :log="eventLog"
      :opener="devOpener"
      @close="devOpen = false"
      @reset="reset"
    />
  </div>
</template>

<style scoped>
.chat-view,
.shell {
  width: 100%;
  height: 100dvh;
  overflow: hidden;
}

.chat-view {
  color: var(--c-text);
  font-family: var(--f-body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  background: var(--c-bg);
}

.shell {
  display: flex;
  flex-direction: column;
}
</style>
