<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { NARROW_COMPOSER_MAX_WIDTH_PX, QUERY_MAX_LENGTH } from '../lib/constants'
import { messages } from '../lib/messages'

const model = defineModel<string>({ required: true })
const props = defineProps<{ disabled: boolean; turnCounter: string }>()
const emit = defineEmits<{ send: [query: string] }>()

const input = ref<HTMLInputElement | null>(null)
const narrow = ref(false)
let mediaQuery: MediaQueryList | null = null

const sendDisabled = computed(() => props.disabled || model.value.trim().length === 0)
const placeholder = computed(() => {
  if (narrow.value) return messages.messageInputNarrowPlaceholder
  return messages.messageInputPlaceholder
})
const buttonLabel = computed(() => {
  if (props.disabled) return messages.messageInputWorking
  return messages.messageInputSend
})

function updateWidth(event?: MediaQueryListEvent): void {
  narrow.value = event?.matches ?? mediaQuery?.matches ?? false
}

function submit(event?: KeyboardEvent): void {
  if (event?.isComposing || props.disabled) return
  const query = model.value.trim()
  if (query) emit('send', query)
}

function focus(): void {
  input.value?.focus()
}

onMounted(() => {
  mediaQuery = window.matchMedia(`(max-width: ${NARROW_COMPOSER_MAX_WIDTH_PX}px)`)
  updateWidth()
  mediaQuery.addEventListener('change', updateWidth)
})

onUnmounted(() => mediaQuery?.removeEventListener('change', updateWidth))
defineExpose({ focus })
</script>

<template>
  <div class="composer">
    <div class="inner">
      <div class="field">
        <input
          ref="input"
          v-model="model"
          :aria-label="messages.messageInputAriaLabel"
          :maxlength="QUERY_MAX_LENGTH"
          :placeholder="placeholder"
          @keydown.enter.prevent="submit($event)"
        />
        <button class="send" type="button" :disabled="sendDisabled" @click="submit()">
          {{ buttonLabel }}
        </button>
      </div>
      <div class="hints">
        <span class="hint">
          {{ messages.messageInputHint }}
        </span>
        <span>{{ turnCounter }}</span>
      </div>
    </div>
  </div>
</template>

<style scoped>
.composer {
  flex: none;
  padding: var(--s-5) var(--gutter) calc(20px + env(safe-area-inset-bottom));
  background: var(--c-bg);
  border-top: 1px solid var(--c-line);
}

.inner {
  max-width: var(--measure);
  margin: 0 auto;
}

.field {
  display: flex;
  gap: var(--s-4);
  align-items: flex-end;
  padding: var(--s-3) var(--s-3) var(--s-3) var(--s-6);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--r-lg);
  transition: border-color var(--dur-fast) ease;
}

.field:focus-within {
  border-color: var(--c-focus-border);
}

input {
  flex: 1;
  min-width: 0;
  padding: var(--s-3) 0;
  color: var(--c-text);
  background: transparent;
  border: 0;
  outline: none;
}

input::placeholder {
  color: var(--c-text-faint);
}

.send {
  flex: none;
  min-height: var(--tap-min);
  padding: var(--s-4) var(--s-7);
  color: var(--c-on-accent);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  background: var(--c-accent);
  border: 0;
  border-radius: var(--r-md);
  transition: background var(--dur-fast) ease;
}

.send:hover:not(:disabled) {
  background: var(--c-accent-hover);
}

.hints {
  display: flex;
  gap: var(--s-6);
  justify-content: space-between;
  margin-top: 9px;
  color: var(--c-text-ghost);
  font-family: var(--f-mono);
  font-size: 10.5px;
}

@media (max-width: 760px) {
  .hint {
    display: none;
  }
}
</style>
