<template>
  <div class="th-page conversation-view">
    <header class="page-heading">
      <div>
        <h1>{{ t('conversation.title') }}</h1>
        <p>{{ t('conversation.subtitle') }}</p>
      </div>
    </header>

    <section class="th-section conversation-section">
      <div
        v-if="loading"
        class="empty-state"
      >
        {{ t('common.loading') }}
      </div>
      <div
        v-else-if="errorMessage"
        class="empty-state empty-state--error"
      >
        {{ errorMessage }}
      </div>
      <div
        v-else-if="!conversation"
        class="empty-state"
      >
        {{ t('common.noData') }}
      </div>

      <template v-else>
        <header class="conversation-summary">
          <div>
            <p>{{ t('conversation.subject') }}</p>
            <h2>{{ conversation.conversation.subject ?? '-' }}</h2>
          </div>
          <dl>
            <div>
              <dt>{{ t('conversation.messageCount') }}</dt>
              <dd>{{ conversation.conversation.message_count }}</dd>
            </div>
            <div>
              <dt>{{ t('conversation.channel') }}</dt>
              <dd>{{ conversation.conversation.channel }}</dd>
            </div>
            <div>
              <dt>{{ t('conversation.firstReceivedAt') }}</dt>
              <dd>{{ formatReservationDateTime(conversation.conversation.first_received_at) }}</dd>
            </div>
            <div>
              <dt>{{ t('conversation.lastReceivedAt') }}</dt>
              <dd>{{ formatReservationDateTime(conversation.conversation.last_received_at) }}</dd>
            </div>
          </dl>
        </header>

        <div class="message-stack">
          <article
            v-for="message in conversation.messages"
            :key="message.id"
            class="message-card"
          >
            <header class="message-card__header">
              <div>
                <p>{{ message.sender_summary ?? '-' }}</p>
                <h3>{{ message.subject ?? conversation.conversation.subject ?? '-' }}</h3>
              </div>
              <span>{{ formatReservationDateTime(message.received_at ?? message.source_sent_at) }}</span>
            </header>

            <!-- eslint-disable vue/no-v-html -->
            <div
              v-if="renderableHtmlBody(message)"
              class="message-body message-body--html"
              v-html="renderableHtmlBody(message)"
            />
            <!-- eslint-enable vue/no-v-html -->
            <pre
              v-else
              class="message-body"
            >{{ formatTextMessageBody(message) }}</pre>

            <section
              v-if="message.attachments.length || message.inline_images.length"
              class="message-related"
            >
              <h4>{{ t('conversation.attachments') }}</h4>
              <ul>
                <li
                  v-for="(media, mediaIndex) in combinedMedia(message)"
                  :key="mediaKey(media, mediaIndex)"
                >
                  <a
                    v-if="safeExternalUrl(media.externalUrl)"
                    :href="safeExternalUrl(media.externalUrl)"
                    target="_blank"
                    rel="noopener noreferrer"
                  >
                    {{ media.fileName ?? media.externalMediaId ?? t('conversation.unnamedAttachment') }}
                  </a>
                  <span v-else>
                    {{ media.fileName ?? media.externalMediaId ?? t('conversation.unnamedAttachment') }}
                  </span>
                  <small>{{ formatMediaSize(media.sizeBytes) }}</small>
                </li>
              </ul>
            </section>

            <section
              v-if="message.related_orders.length || message.related_tasks.length"
              class="message-related message-related--grid"
            >
              <div>
                <h4>{{ t('conversation.relatedOrders') }}</h4>
                <ul>
                  <li
                    v-for="order in message.related_orders"
                    :key="order.order_id"
                  >
                    <RouterLink :to="`/reservation/orders/${order.order_id}`">
                      {{ order.display_order_key ?? order.order_id }}
                    </RouterLink>
                  </li>
                </ul>
              </div>
              <div>
                <h4>{{ t('conversation.relatedTasks') }}</h4>
                <ul>
                  <li
                    v-for="task in message.related_tasks"
                    :key="task.task_id"
                  >
                    <RouterLink :to="`/reservation/tasks/${task.task_id}`">
                      {{ formatReservationTaskCardLabel(t, task) }}
                    </RouterLink>
                  </li>
                </ul>
              </div>
            </section>
          </article>
        </div>
      </template>
    </section>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'

import { fetchSourceMessageConversation } from '@/services/reservationService'
import type {
  SourceMessageConversationItem,
  SourceMessageConversationResult,
  SourceMessageOriginalMedia,
} from '@/types/reservation'
import { formatReservationTaskCardLabel } from '@/utils/reservationDisplay'
import { formatReservationDateTime } from '@/utils/reservationFormat'

const route = useRoute()
const { t } = useI18n()
const loading = ref(false)
const errorMessage = ref('')
const conversation = ref<SourceMessageConversationResult | null>(null)
const sourceMessageId = computed(() => String(route.params.sourceMessageId ?? ''))
let conversationRequestSequence = 0

watch(
  sourceMessageId,
  (nextSourceMessageId) => {
    void loadConversation(nextSourceMessageId)
  },
  { immediate: true },
)

async function loadConversation(nextSourceMessageId: string): Promise<void> {
  const requestSequence = ++conversationRequestSequence
  if (!nextSourceMessageId) {
    conversation.value = null
    return
  }
  loading.value = true
  errorMessage.value = ''
  conversation.value = null
  try {
    const result = await fetchSourceMessageConversation(nextSourceMessageId)
    if (requestSequence !== conversationRequestSequence) {
      return
    }
    conversation.value = normalizeConversation(result)
  } catch (error) {
    if (requestSequence !== conversationRequestSequence) {
      return
    }
    errorMessage.value = error instanceof Error ? error.message : String(error)
  } finally {
    if (requestSequence === conversationRequestSequence) {
      loading.value = false
    }
  }
}

function normalizeConversation(result: SourceMessageConversationResult): SourceMessageConversationResult {
  return {
    conversation: result.conversation,
    messages: result.messages.map((message) => ({
      ...message,
      attachments: message.attachments ?? [],
      html_body_sanitized: message.html_body_sanitized ?? null,
      html_render_mode: message.html_render_mode ?? null,
      inline_images: message.inline_images ?? [],
      related_orders: message.related_orders ?? [],
      related_tasks: message.related_tasks ?? [],
    })),
  }
}

function formatTextMessageBody(message: SourceMessageConversationItem): string {
  return message.text_body || t('conversation.emptyBody')
}

function renderableHtmlBody(message: SourceMessageConversationItem): string {
  if (message.html_render_mode && message.html_render_mode !== 'SANITIZED_HTML') {
    return ''
  }
  return message.html_body_sanitized ?? ''
}

function combinedMedia(message: SourceMessageConversationItem): SourceMessageOriginalMedia[] {
  return [...message.attachments, ...message.inline_images]
}

function mediaKey(media: SourceMessageOriginalMedia, index: number): string {
  return `${media.externalMediaId ?? media.fileName ?? media.mediaType ?? 'media'}-${index}`
}

function safeExternalUrl(value: string | null): string | undefined {
  if (!value) {
    return undefined
  }
  try {
    const url = new URL(value)
    return url.protocol === 'http:' || url.protocol === 'https:' ? value : undefined
  } catch {
    return undefined
  }
}

function formatMediaSize(sizeBytes: number | null): string {
  if (!sizeBytes) {
    return '-'
  }
  if (sizeBytes < 1024) {
    return `${sizeBytes} B`
  }
  if (sizeBytes < 1024 * 1024) {
    return `${Math.round(sizeBytes / 1024)} KB`
  }
  return `${(sizeBytes / 1024 / 1024).toFixed(1)} MB`
}
</script>

<style scoped>
.conversation-view {
  max-width: 1180px;
}

.page-heading h1,
.page-heading p {
  margin: 0;
}

.page-heading h1 {
  color: var(--th-color-slate-900);
  font-size: 28px;
}

.page-heading p {
  margin-top: 8px;
  color: var(--th-color-slate-500);
  font-size: 14px;
}

.conversation-section {
  display: grid;
  gap: 18px;
  min-height: 360px;
  padding: 20px;
}

.empty-state {
  min-height: 320px;
  display: grid;
  place-items: center;
  gap: 8px;
  color: var(--th-color-slate-500);
  font-size: 13px;
  padding: 24px;
  text-align: center;
}

.empty-state strong {
  color: var(--th-color-slate-900);
}

.empty-state--error {
  color: var(--th-color-danger);
}

.conversation-summary {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 2fr);
  gap: 20px;
  align-items: start;
}

.conversation-summary p,
.conversation-summary h2,
.conversation-summary dl {
  margin: 0;
}

.conversation-summary p {
  color: var(--th-color-blue-600);
  font-size: 12px;
  font-weight: 800;
}

.conversation-summary h2 {
  margin-top: 5px;
  color: var(--th-color-slate-900);
  font-size: 22px;
  line-height: 1.25;
}

.conversation-summary dl {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.conversation-summary dt {
  color: var(--th-color-slate-500);
  font-size: 12px;
  font-weight: 700;
}

.conversation-summary dd {
  margin: 5px 0 0;
  color: var(--th-color-slate-900);
  font-size: 13px;
  font-weight: 800;
}

.message-stack {
  display: grid;
  gap: 14px;
}

.message-card {
  display: grid;
  gap: 14px;
  border: 1px solid var(--th-color-slate-200);
  border-radius: var(--th-radius-md);
  background: var(--th-color-white);
  padding: 16px;
}

.message-card__header {
  display: flex;
  justify-content: space-between;
  gap: 14px;
}

.message-card__header p,
.message-card__header h3 {
  margin: 0;
}

.message-card__header p,
.message-card__header span {
  color: var(--th-color-slate-500);
  font-size: 12px;
  font-weight: 700;
}

.message-card__header h3 {
  margin-top: 5px;
  color: var(--th-color-slate-900);
  font-size: 16px;
}

.message-body {
  max-height: none;
  margin: 0;
  overflow-x: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  border: 1px solid var(--th-color-slate-200);
  border-radius: var(--th-radius-sm);
  background: var(--th-color-slate-50);
  color: var(--th-color-slate-900);
  font-family: inherit;
  font-size: 13px;
  line-height: 1.7;
  padding: 12px;
}

.message-body--html {
  display: block;
}

.message-body--html :deep(*) {
  max-width: 100%;
}

.message-body--html :deep(img) {
  height: auto;
}

.message-related {
  display: grid;
  gap: 8px;
}

.message-related--grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.message-related h4,
.message-related ul {
  margin: 0;
}

.message-related h4 {
  color: var(--th-color-slate-900);
  font-size: 13px;
}

.message-related ul {
  display: grid;
  gap: 6px;
  list-style: none;
  padding: 0;
}

.message-related li {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  color: var(--th-color-slate-700);
  font-size: 13px;
}

.message-related a {
  color: var(--th-color-blue-600);
  font-weight: 800;
  text-decoration: none;
}

.message-related small {
  color: var(--th-color-slate-500);
}

@media (max-width: 860px) {
  .conversation-summary,
  .message-related--grid {
    grid-template-columns: 1fr;
  }

  .message-card__header {
    display: grid;
  }
}
</style>
