<template>
  <section class="th-section task-detail-panel">
    <div
      v-if="loading"
      class="task-state"
    >
      {{ t('common.loading') }}
    </div>
    <div
      v-else-if="errorMessage"
      class="task-state task-state--error"
    >
      {{ errorMessage }}
    </div>

    <template v-else-if="detail">
      <header class="task-detail-header">
        <div>
          <p class="task-eyebrow">
            {{ formatReservationTaskRouteSummary(t, detail) }}
          </p>
          <h1>{{ t('task.title') }} {{ detail.task_id }}</h1>
          <span class="task-contract">
            {{ t('task.fieldContract') }}: {{ detail.field_contract_version }}
          </span>
          <div class="task-links">
            <RouterLink
              v-if="!isReadonlyDiagnosticDetail"
              class="task-link"
              :to="`/reservation/orders/${detail.order_id}`"
            >
              {{ t('task.viewOrder') }} {{ detail.order_id }}
            </RouterLink>
            <span
              v-if="!detail.source_message_id"
              class="task-link task-link--disabled"
            >
              {{ t('task.viewConversation') }} · {{ t('common.endpointMissing') }}
            </span>
            <RouterLink
              v-else
              class="task-link"
              :to="`/reservation/source-messages/${detail.source_message_id}/conversation`"
            >
              {{ t('task.viewConversation') }}
            </RouterLink>
          </div>
        </div>
        <ReservationStatusBadge :status="detail.task_status" />
      </header>

      <section class="task-evidence">
        <h2>{{ t('task.evidence') }}</h2>
        <dl>
          <div>
            <dt>{{ t('task.sourceMessageId') }}</dt>
            <dd class="th-code">
              {{ detail.source_message_id || '-' }}
            </dd>
          </div>
          <div>
            <dt>{{ t('task.sourceSubject') }}</dt>
            <dd>
              {{ detail.source_subject ?? t('task.sourceSummaryPending') }}
            </dd>
          </div>
          <div>
            <dt>{{ t('task.sourceSender') }}</dt>
            <dd>{{ detail.source_sender_summary ?? '-' }}</dd>
          </div>
          <div>
            <dt>{{ t('task.sourceReceivedAt') }}</dt>
            <dd>{{ formatReservationDateTime(detail.source_received_at) }}</dd>
          </div>
        </dl>
      </section>

      <section class="task-route-info">
        <h2>{{ t('task.routeInfo') }}</h2>
        <dl>
          <div>
            <dt>{{ t('task.systemTaskType') }}</dt>
            <dd>
              {{ formatReservationTaskCard(t, detail.system_task_type) }}
              <small class="th-code">{{ detail.system_task_type }}</small>
            </dd>
          </div>
          <div>
            <dt>{{ t('task.taskCardType') }}</dt>
            <dd>
              {{ formatReservationTaskCardLabel(t, detail) }}
              <small class="th-code">{{ detail.task_card_type }}</small>
            </dd>
          </div>
          <div>
            <dt>{{ t('task.resultType') }}</dt>
            <dd>{{ detail.result_type ? formatReservationResultType(t, detail.result_type) : '-' }}</dd>
          </div>
          <div>
            <dt>{{ t('task.aiTaskType') }}</dt>
            <dd>{{ detail.ai_task_type || '-' }}</dd>
          </div>
          <div>
            <dt>{{ t('task.taskSubtype') }}</dt>
            <dd>{{ detail.task_subtype ? formatReservationRouteCode(t, detail.task_subtype) : '-' }}</dd>
          </div>
          <div>
            <dt>{{ t('task.routeCode') }}</dt>
            <dd class="th-code">
              {{ detail.route_code || '-' }}
            </dd>
          </div>
          <div>
            <dt>{{ t('task.systemProcessCategory') }}</dt>
            <dd>{{ detail.system_process_category ? formatReservationSystemProcessCategory(t, detail.system_process_category) : '-' }}</dd>
          </div>
          <div>
            <dt>{{ t('task.reviewStatus') }}</dt>
            <dd>{{ detail.review_status || '-' }}</dd>
          </div>
        </dl>
      </section>

      <section
        v-if="isSourceMessageOnlyDetail"
        class="source-only-result"
      >
        <header>
          <h2>{{ t('task.sourceOnlyResult') }}</h2>
          <p>{{ t('task.sourceOnlyReadonly') }}</p>
        </header>
        <dl v-if="sourceMessageOnlyResult">
          <div>
            <dt>{{ t('task.entryResultCode') }}</dt>
            <dd class="th-code">
              {{ sourceMessageOnlyResult.entry_result_code || '-' }}
            </dd>
          </div>
          <div>
            <dt>{{ t('task.entryResultMeaning') }}</dt>
            <dd>{{ sourceMessageOnlyResult.entry_result_meaning || '-' }}</dd>
          </div>
          <div>
            <dt>{{ t('task.entryResultDescription') }}</dt>
            <dd>{{ sourceMessageOnlyResult.entry_result_description || '-' }}</dd>
          </div>
          <div>
            <dt>{{ t('task.entryResultSourceMessageId') }}</dt>
            <dd class="th-code">
              {{ sourceMessageOnlyResult.entry_result_source_message_id || '-' }}
            </dd>
          </div>
          <div class="source-only-result__raw">
            <dt>{{ t('task.rawAnswer') }}</dt>
            <dd>
              <pre>{{ sourceMessageOnlyResult.raw_answer || '-' }}</pre>
            </dd>
          </div>
          <div class="source-only-result__raw">
            <dt>{{ t('task.agentAssessment') }}</dt>
            <dd>
              <pre>{{ formatJson(sourceMessageOnlyResult.agent_assessment) }}</pre>
            </dd>
          </div>
          <div class="source-only-result__raw">
            <dt>{{ t('task.sourceNotification') }}</dt>
            <dd>
              <pre>{{ formatJson(sourceMessageOnlyResult.notification) }}</pre>
            </dd>
          </div>
          <div class="source-only-result__raw">
            <dt>{{ t('task.manualReviewData') }}</dt>
            <dd>
              <pre>{{ formatJson(sourceMessageOnlyResult.manual_review) }}</pre>
            </dd>
          </div>
        </dl>
        <p
          v-else
          class="source-only-result__empty"
        >
          {{ t('task.noSourceOnlyResult') }}
        </p>
      </section>

      <section
        v-if="manualReviewRecord"
        class="task-ai-diagnostics"
      >
        <h2>{{ t('task.manualReviewData') }}</h2>
        <pre>{{ formatJson(manualReviewRecord) }}</pre>
      </section>

      <section
        v-if="manualReviewEvidence"
        class="manual-review-evidence"
      >
        <header>
          <h2>{{ t('task.manualReviewEvidence') }}</h2>
          <p>{{ manualReviewEvidence.visibleReason || t('task.manualReviewEvidenceFallback') }}</p>
        </header>
        <dl>
          <div>
            <dt>{{ t('task.manualResolution.reasonCode') }}</dt>
            <dd class="th-code">
              {{ manualReviewEvidence.reasonCode || '-' }}
            </dd>
          </div>
          <div>
            <dt>{{ t('task.manualReviewMissingFields') }}</dt>
            <dd>{{ formatStringList(manualReviewEvidence.missingFields) }}</dd>
          </div>
          <div>
            <dt>{{ t('task.manualReviewBlockingPoints') }}</dt>
            <dd>{{ formatStringList(manualReviewEvidence.blockingPoints) }}</dd>
          </div>
          <div>
            <dt>{{ t('task.manualReviewConflictingPoints') }}</dt>
            <dd>{{ formatStringList(manualReviewEvidence.conflictingPoints) }}</dd>
          </div>
          <div>
            <dt>{{ t('task.manualReviewSuggestedActions') }}</dt>
            <dd>{{ formatStringList(manualReviewEvidence.suggestedHumanActions) }}</dd>
          </div>
          <div>
            <dt>{{ t('task.manualReviewEvidenceToCheck') }}</dt>
            <dd>{{ formatStringList(manualReviewEvidence.evidenceToCheck) }}</dd>
          </div>
        </dl>
        <div class="manual-review-evidence__candidates">
          <h3>{{ t('task.parentIdentityCandidates') }}</h3>
          <div
            v-if="manualReviewEvidence.parentIdentityCandidates.length"
            class="candidate-grid"
          >
            <article
              v-for="(candidate, index) in manualReviewEvidence.parentIdentityCandidates"
              :key="`${candidate.title}-${candidate.summary}-${index}`"
              class="candidate-item"
            >
              <strong>{{ candidate.title || t('task.parentIdentityCandidate') }}</strong>
              <span class="th-code">{{ candidate.summary || '-' }}</span>
              <small>{{ candidate.sourceSummary || '-' }}</small>
              <dl
                v-if="candidate.attributes.length"
                class="candidate-item__attributes"
              >
                <div
                  v-for="attribute in candidate.attributes"
                  :key="attribute.key"
                >
                  <dt>{{ attribute.key }}</dt>
                  <dd>{{ attribute.value }}</dd>
                </div>
              </dl>
            </article>
          </div>
          <p
            v-else
            class="manual-review-evidence__empty"
          >
            {{ t('task.noParentIdentityCandidates') }}
          </p>
        </div>
      </section>

      <section
        v-if="adapterContractErrors.length"
        class="task-ai-diagnostics task-ai-diagnostics--error"
      >
        <h2>{{ t('task.adapterContractErrors') }}</h2>
        <article
          v-for="item in adapterContractErrors"
          :key="item.transition_id || `${item.route_code}-${item.array_index}`"
        >
          <strong>{{ item.adapter_error_code || item.route_code || t('resultType.ADAPTER_CONTRACT_ERROR') }}</strong>
          <p>{{ item.adapter_error_message || '-' }}</p>
          <pre>{{ formatJson(item.payload_fragment) }}</pre>
        </article>
      </section>

      <section
        v-if="unhandledIntents.length"
        class="task-ai-diagnostics"
      >
        <h2>{{ t('task.unhandledIntents') }}</h2>
        <article
          v-for="item in unhandledIntents"
          :key="item.transition_id || `${item.route_code}-${item.array_index}`"
        >
          <strong>{{ item.route_code || item.task_subtype || t('resultType.UNHANDLED_CURRENT_INTENT') }}</strong>
          <pre>{{ formatJson(item.payload_fragment) }}</pre>
        </article>
      </section>

      <div
        v-if="detail.availability.blocked"
        class="task-alert"
      >
        <i
          class="pi pi-lock"
          aria-hidden="true"
        />
        {{ formatReservationMaybeStableReason(t, detail.availability.blocked_reason, 'task.blocked') }}
      </div>

      <div
        v-if="notice"
        class="task-notice"
      >
        {{ notice }}
      </div>
      <div
        v-if="actionErrorMessage"
        class="task-alert"
      >
        <i
          class="pi pi-exclamation-triangle"
          aria-hidden="true"
        />
        {{ actionErrorMessage }}
      </div>

      <div class="task-detail-body">
        <div class="task-card-main">
          <ReservationManualReviewResolutionPanel
            v-if="isTypeKnownManualReviewDetail"
            :task-order-id="detail.order_id"
            :review-status="detail.review_status"
            :review-resolution="detail.review_resolution"
            :manual-review="detail.manual_review"
            :fields="detail.fields"
            :field-values="fieldValues"
            :busy="mutationBusy !== null"
            @submit="submitManualReviewResolution"
          />

          <ReservationManualReviewConversionPanel
            v-if="isFallbackManualReviewDetail && !isTypeKnownManualReviewDetail && !isReadonlyDiagnosticDetail"
            :busy="mutationBusy !== null"
            @submit="submitManualReviewConversion"
          />

          <ReservationTaskFieldRenderer
            v-model="fieldValues"
            :fields="detail.fields"
            :read-only="isFieldRendererReadOnly"
            :validation-errors="validationErrorMessages"
          />

          <footer
            v-if="canSave || canConfirm"
            class="task-actions"
          >
            <button
              v-if="canEditTask"
              type="button"
              class="secondary-button"
              :disabled="!canSave || mutationBusy !== null"
              @click="saveDraft"
            >
              <i
                class="pi pi-save"
                aria-hidden="true"
              />
              {{ t('task.saveDraft') }}
            </button>
            <button
              v-if="canConfirmTask"
              type="button"
              class="primary-button"
              :disabled="!canConfirm || mutationBusy !== null"
              @click="confirmTask"
            >
              <i
                class="pi pi-check"
                aria-hidden="true"
              />
              {{ t('task.confirm') }}
            </button>
          </footer>
        </div>

        <aside class="task-side-stack">
          <ReservationOperaPanel
            v-if="!isReadonlyDiagnosticDetail && !isPendingTypeKnownManualReviewDetail"
            :task-id="detail.task_id"
            :operations="detail.opera_operations"
            :executable="canExecuteOpera"
            :show-actions="canExecuteOpera"
            @operation-updated="replaceOperation"
          />
          <ReservationAuditTimeline
            v-if="canViewAudit"
            :items="audits"
          />
        </aside>
      </div>
    </template>
  </section>
</template>

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

import ReservationAuditTimeline from '@/components/reservation/ReservationAuditTimeline.vue'
import ReservationManualReviewConversionPanel from '@/components/reservation/ReservationManualReviewConversionPanel.vue'
import ReservationManualReviewResolutionPanel from '@/components/reservation/ReservationManualReviewResolutionPanel.vue'
import ReservationOperaPanel from '@/components/reservation/ReservationOperaPanel.vue'
import ReservationStatusBadge from '@/components/reservation/ReservationStatusBadge.vue'
import ReservationTaskFieldRenderer from '@/components/reservation/ReservationTaskFieldRenderer.vue'
import {
  convertReservationManualReviewTask,
  confirmReservationTask,
  fetchReservationTaskAudits,
  fetchReservationTaskDetail,
  resolveReservationManualReviewTask,
  saveReservationTaskDraft,
} from '@/services/reservationService'
import type {
  ReservationManualReviewConversionRequest,
  ReservationManualReviewResolutionRequest,
  ReservationOperaOperationResult,
  ReservationRecord,
  ReservationTaskAuditLogResult,
  ReservationTaskDetailResult,
} from '@/types/reservation'
import {
  buildEditableFieldValues,
  buildInitialFieldValues,
  validateReservationFieldValues,
  type ReservationFieldValidationErrors,
} from '@/utils/reservationFieldRules'
import {
  formatReservationResultType,
  formatReservationRouteCode,
  formatReservationMaybeStableReason,
  formatReservationSystemProcessCategory,
  formatReservationTaskCard,
  formatReservationTaskCardLabel,
  formatReservationTaskRouteSummary,
  isReservationFallbackManualReviewTask,
  isReservationReadonlyDiagnosticTask,
  isReservationSourceMessageNotificationTask,
  isReservationTypeKnownManualReviewTask,
  normalizeStableCode,
} from '@/utils/reservationDisplay'
import { formatReservationDateTime } from '@/utils/reservationFormat'
import { useAuthStore } from '@/stores/authStore'

const props = defineProps<{
  taskId: string
}>()

const { t } = useI18n()
const authStore = useAuthStore()
const loading = ref(false)
const errorMessage = ref('')
const actionErrorMessage = ref('')
const notice = ref('')
const detail = ref<ReservationTaskDetailResult | null>(null)
const audits = ref<ReservationTaskAuditLogResult[]>([])
const fieldValues = ref<ReservationRecord>({})
const validationErrors = ref<ReservationFieldValidationErrors>({})
const validationSummaryActive = ref(false)
const mutationBusy = ref<'save' | 'confirm' | 'manual-convert' | 'manual-resolve' | null>(null)

const isSourceMessageOnlyDetail = computed(() =>
  isReservationSourceMessageNotificationTask(detail.value),
)
const isReadonlyDiagnosticDetail = computed(() => isReservationReadonlyDiagnosticTask(detail.value))
const isTypeKnownManualReviewDetail = computed(() => isReservationTypeKnownManualReviewTask(detail.value))
const isPendingTypeKnownManualReviewDetail = computed(
  () => isTypeKnownManualReviewDetail.value && normalizeStableCode(detail.value?.review_status) !== 'RESOLVED',
)
const isFallbackManualReviewDetail = computed(() => isReservationFallbackManualReviewTask(detail.value))
const sourceMessageOnlyResult = computed(() => detail.value?.source_message_only_result ?? null)
const manualReviewRecord = computed(() => detail.value?.manual_review ?? sourceMessageOnlyResult.value?.manual_review ?? null)
const manualReviewEvidence = computed(() => buildManualReviewEvidence(detail.value, manualReviewRecord.value))
const adapterContractErrors = computed(() => detail.value?.adapter_contract_errors ?? [])
const unhandledIntents = computed(() => detail.value?.unhandled_intents ?? [])
const isFieldRendererReadOnly = computed(
  () =>
    Boolean(detail.value?.availability.read_only) ||
    Boolean(detail.value?.availability.blocked) ||
    isReadonlyDiagnosticDetail.value ||
    isPendingTypeKnownManualReviewDetail.value,
)
const canEditTask = computed(
  () =>
    authStore.hasPermission('RESERVATION_TASK_EDIT') &&
    !isReadonlyDiagnosticDetail.value &&
    !isPendingTypeKnownManualReviewDetail.value &&
    !detail.value?.availability.blocked &&
    !detail.value?.availability.read_only,
)
const canConfirmTask = computed(
  () =>
    authStore.hasPermission('RESERVATION_TASK_CONFIRM') &&
    !isReadonlyDiagnosticDetail.value &&
    !isPendingTypeKnownManualReviewDetail.value &&
    !detail.value?.availability.blocked &&
    !detail.value?.availability.read_only,
)
const canExecuteOpera = computed(
  () =>
    authStore.hasPermission('RESERVATION_OPERA_SIM_EXECUTE') &&
    !isReadonlyDiagnosticDetail.value &&
    !isPendingTypeKnownManualReviewDetail.value &&
    !detail.value?.availability.blocked &&
    !detail.value?.availability.read_only &&
    Boolean(detail.value?.availability.executable),
)
const canViewAudit = computed(() => authStore.hasPermission('RESERVATION_AUDIT_READ'))
const canSave = computed(() => canEditTask.value && Boolean(detail.value?.availability.editable))
const canConfirm = computed(() => canConfirmTask.value && Boolean(detail.value?.availability.confirmable))
const validationErrorMessages = computed(() => toValidationErrorMessages(validationErrors.value))

watch(
  () => props.taskId,
  (taskId) => {
    void loadTask(taskId)
  },
  { immediate: true },
)

watch(
  fieldValues,
  () => {
    refreshValidationAfterFieldChange()
  },
  { deep: true },
)

async function loadTask(taskId: string): Promise<void> {
  loading.value = true
  errorMessage.value = ''
  actionErrorMessage.value = ''
  notice.value = ''
  try {
    const [nextDetail, nextAudits] = await Promise.all([
      fetchReservationTaskDetail(taskId),
      canViewAudit.value
        ? fetchReservationTaskAudits(taskId)
        : Promise.resolve({
            task_id: taskId,
            items: [],
          }),
    ])
    applyTaskData(nextDetail, nextAudits.items)
  } catch (error) {
    errorMessage.value = error instanceof Error ? error.message : String(error)
  } finally {
    loading.value = false
  }
}

async function saveDraft(): Promise<void> {
  await mutateTaskPayload('save')
}

async function confirmTask(): Promise<void> {
  await mutateTaskPayload('confirm')
}

async function submitManualReviewConversion(request: ReservationManualReviewConversionRequest): Promise<void> {
  if (!detail.value) {
    return
  }
  actionErrorMessage.value = ''
  notice.value = ''
  if (request.target_task_type !== 'NEW_BOOKING' && !request.target_order_id) {
    actionErrorMessage.value = t('task.manualConversion.targetOrderRequired')
    return
  }

  mutationBusy.value = 'manual-convert'
  try {
    const result = await convertReservationManualReviewTask(detail.value.task_id, request)
    const [nextDetail, nextAudits] = await Promise.all([
      fetchReservationTaskDetail(detail.value.task_id),
      canViewAudit.value
        ? fetchReservationTaskAudits(detail.value.task_id)
        : Promise.resolve({
            task_id: detail.value.task_id,
            items: [],
          }),
    ])
    applyTaskData(nextDetail, nextAudits.items)
    notice.value = t('task.manualConversion.succeeded', {
      taskType: formatReservationTaskCard(t, result.task_card_type),
      orderId: result.order_id,
    })
  } catch (error) {
    actionErrorMessage.value = error instanceof Error ? error.message : String(error)
  } finally {
    mutationBusy.value = null
  }
}

async function submitManualReviewResolution(request: ReservationManualReviewResolutionRequest): Promise<void> {
  if (!detail.value) {
    return
  }
  actionErrorMessage.value = ''
  notice.value = ''
  mutationBusy.value = 'manual-resolve'
  try {
    await resolveReservationManualReviewTask(detail.value.task_id, request)
    const [nextDetail, nextAudits] = await Promise.all([
      fetchReservationTaskDetail(detail.value.task_id),
      canViewAudit.value
        ? fetchReservationTaskAudits(detail.value.task_id)
        : Promise.resolve({
            task_id: detail.value.task_id,
            items: [],
          }),
    ])
    applyTaskData(nextDetail, nextAudits.items)
    notice.value = t('task.manualResolution.succeeded')
  } catch (error) {
    actionErrorMessage.value = error instanceof Error ? error.message : String(error)
  } finally {
    mutationBusy.value = null
  }
}

async function mutateTaskPayload(action: 'save' | 'confirm'): Promise<void> {
  if (!detail.value) {
    return
  }
  if ((action === 'save' && !canSave.value) || (action === 'confirm' && !canConfirm.value)) {
    return
  }
  const taskId = detail.value.task_id
  mutationBusy.value = action
  actionErrorMessage.value = ''
  validationSummaryActive.value = false
  notice.value = ''
  try {
    if (action === 'confirm' && !validateBeforeConfirm(detail.value)) {
      return
    }
    const request = {
      field_values: buildEditableFieldValues(detail.value.fields, fieldValues.value, detail.value.availability.read_only),
    }
    const result =
      action === 'save' ? await saveReservationTaskDraft(taskId, request) : await confirmReservationTask(taskId, request)
    detail.value = {
      ...detail.value,
      task_status: result.task_status,
      draft_payload: result.draft_payload,
      confirmed_payload: result.confirmed_payload,
      opera_operations: result.opera_operations,
    }
    if (canViewAudit.value) {
      await refreshAudits(taskId)
    }
    notice.value = action === 'save' ? t('task.savedLocally') : t('task.confirmedLocally')
  } catch (error) {
    actionErrorMessage.value = error instanceof Error ? error.message : String(error)
  } finally {
    mutationBusy.value = null
  }
}

function applyTaskData(nextDetail: ReservationTaskDetailResult, nextAudits: ReservationTaskAuditLogResult[]): void {
  detail.value = nextDetail
  audits.value = nextAudits
  fieldValues.value = buildInitialFieldValues(
    nextDetail.fields,
    nextDetail.draft_payload ?? nextDetail.confirmed_payload,
    nextDetail.field_contract_version,
  )
  validationErrors.value = {}
  validationSummaryActive.value = false
}

function validateBeforeConfirm(taskDetail: ReservationTaskDetailResult): boolean {
  const nextErrors = validateReservationFieldValues(taskDetail.fields, fieldValues.value)
  validationErrors.value = nextErrors
  if (hasValidationErrors(nextErrors)) {
    actionErrorMessage.value = t('task.validationFailed')
    validationSummaryActive.value = true
    return false
  }
  return true
}

function refreshValidationAfterFieldChange(): void {
  if (!detail.value || (!hasValidationErrors(validationErrors.value) && !validationSummaryActive.value)) {
    return
  }
  const nextErrors = validateReservationFieldValues(detail.value.fields, fieldValues.value)
  validationErrors.value = nextErrors
  if (!hasValidationErrors(nextErrors) && validationSummaryActive.value) {
    actionErrorMessage.value = ''
    validationSummaryActive.value = false
  }
}

function hasValidationErrors(errors: ReservationFieldValidationErrors): boolean {
  return Object.keys(errors).length > 0
}

function toValidationErrorMessages(errors: ReservationFieldValidationErrors): Record<string, string> {
  return Object.fromEntries(
    Object.entries(errors).map(([fieldPath, error]) => [
      fieldPath,
      t(`task.validation.${error.code}`, { field: error.field_name }),
    ]),
  )
}

async function refreshAudits(taskId: string): Promise<void> {
  const nextAudits = await fetchReservationTaskAudits(taskId)
  audits.value = nextAudits.items
}

function replaceOperation(operation: ReservationOperaOperationResult): void {
  if (!detail.value) {
    return
  }
  detail.value = {
    ...detail.value,
    opera_operations: detail.value.opera_operations.map((item) =>
      item.operation_id === operation.operation_id ? operation : item,
    ),
  }
}

function formatJson(value: unknown): string {
  return JSON.stringify(value ?? {}, null, 2)
}

interface ManualReviewEvidenceCandidate {
  title: string | null
  summary: string | null
  sourceSummary: string | null
  attributes: Array<{
    key: string
    value: string
  }>
}

interface ManualReviewEvidence {
  reasonCode: string | null
  visibleReason: string | null
  missingFields: string[]
  blockingPoints: string[]
  conflictingPoints: string[]
  suggestedHumanActions: string[]
  evidenceToCheck: string[]
  parentIdentityCandidates: ManualReviewEvidenceCandidate[]
}

function buildManualReviewEvidence(
  taskDetail: ReservationTaskDetailResult | null,
  manualReview: ReservationRecord | null | undefined,
): ManualReviewEvidence | null {
  if (!manualReview || normalizeStableCode(readString(manualReview.reason_code)) !== 'TARGET_OBJECT_UNCLEAR') {
    return null
  }
  return {
    reasonCode: readString(manualReview.reason_code),
    visibleReason: readString(manualReview.visible_reason),
    missingFields: readStringArray(manualReview.missing_fields),
    blockingPoints: readStringArray(manualReview.blocking_points),
    conflictingPoints: readStringArray(manualReview.conflicting_points),
    suggestedHumanActions: readStringArray(manualReview.suggested_human_actions),
    evidenceToCheck: readStringArray(manualReview.evidence_to_check),
    parentIdentityCandidates: readParentIdentityCandidates(taskDetail, manualReview),
  }
}

function readParentIdentityCandidates(
  taskDetail: ReservationTaskDetailResult | null,
  manualReview: ReservationRecord,
): ManualReviewEvidenceCandidate[] {
  const contextCandidates = [
    readRecord(taskDetail?.context_used)?.parent_identity_candidates,
    readRecord(manualReview.context_used)?.parent_identity_candidates,
    manualReview.parent_identity_candidates,
  ]
  const candidateList = contextCandidates.find((item) => Array.isArray(item))
  if (!Array.isArray(candidateList)) {
    return []
  }
  return candidateList
    .map((item) => readRecord(item))
    .filter((item): item is ReservationRecord => item !== null)
    .map((item) => {
      const attributes = Object.entries(item)
        .map(([key, value]) => ({
          key,
          value: stringifyCandidateValue(value),
        }))
        .filter((attribute) => attribute.value !== '')
      return {
        title: readFirstString(item, ['field', 'candidate_field', 'key', 'field_path']),
        summary: readFirstString(item, ['value', 'raw_value', 'normalized_value', 'candidate_value']),
        sourceSummary: readFirstString(item, ['evidence_source', 'source', 'evidence', 'origin']),
        attributes,
      }
    })
}

function readRecord(value: unknown): ReservationRecord | null {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return null
  }
  return value as ReservationRecord
}

function readString(value: unknown): string | null {
  return typeof value === 'string' && value.trim() ? value : null
}

function readFirstString(record: ReservationRecord, keys: string[]): string | null {
  for (const key of keys) {
    const value = readString(record[key])
    if (value) {
      return value
    }
  }
  return null
}

function readStringArray(value: unknown): string[] {
  if (!Array.isArray(value)) {
    return []
  }
  return value.filter((item): item is string => typeof item === 'string' && item.trim() !== '')
}

function stringifyCandidateValue(value: unknown): string {
  if (value === undefined || value === null) {
    return ''
  }
  if (typeof value === 'string') {
    return value
  }
  if (typeof value === 'number' || typeof value === 'boolean') {
    return String(value)
  }
  return JSON.stringify(value)
}

function formatStringList(items: string[]): string {
  return items.length ? items.join(' / ') : '-'
}
</script>

<style scoped>
.task-detail-panel {
  display: grid;
  gap: 18px;
  padding: 20px;
}

.task-state {
  min-height: 220px;
  display: grid;
  place-items: center;
  color: var(--th-color-slate-500);
  font-weight: 700;
}

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

.task-detail-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

.task-detail-header h1,
.task-eyebrow {
  margin: 0;
}

.task-eyebrow {
  color: var(--th-color-blue-600);
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
}

.task-detail-header h1 {
  margin-top: 5px;
  color: var(--th-color-slate-900);
  font-size: 22px;
}

.task-contract {
  display: inline-block;
  margin-top: 7px;
  color: var(--th-color-slate-500);
  font-size: 12px;
}

.task-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.task-link {
  color: var(--th-color-blue-600);
  font-size: 12px;
  font-weight: 800;
}

.task-link--disabled {
  color: var(--th-color-slate-500);
}

.task-evidence,
.task-route-info,
.task-ai-diagnostics {
  display: grid;
  gap: 12px;
  border: 1px solid var(--th-color-slate-200);
  border-radius: var(--th-radius-md);
  padding: 16px;
}

.task-route-info {
  background: var(--th-color-slate-50);
}

.task-ai-diagnostics {
  background: var(--th-color-white);
}

.task-ai-diagnostics--error {
  border-color: var(--th-color-danger);
}

.manual-review-evidence {
  display: grid;
  gap: 14px;
  border: 1px solid var(--th-color-warning);
  border-radius: var(--th-radius-md);
  background: color-mix(in srgb, var(--th-color-warning) 8%, var(--th-color-white));
  padding: 16px;
}

.source-only-result {
  display: grid;
  gap: 12px;
  border: 1px solid var(--th-color-blue-100);
  border-radius: var(--th-radius-md);
  background: var(--th-color-blue-100);
  padding: 16px;
}

.task-evidence h2,
.task-evidence dl,
.task-route-info h2,
.task-route-info dl,
.task-ai-diagnostics h2,
.task-ai-diagnostics pre,
.manual-review-evidence h2,
.manual-review-evidence h3,
.manual-review-evidence p,
.manual-review-evidence dl,
.source-only-result h2,
.source-only-result p,
.source-only-result dl {
  margin: 0;
}

.task-evidence h2,
.task-route-info h2,
.task-ai-diagnostics h2,
.manual-review-evidence h2,
.source-only-result h2 {
  color: var(--th-color-slate-900);
  font-size: 15px;
}

.manual-review-evidence header {
  display: grid;
  gap: 6px;
}

.manual-review-evidence header p,
.manual-review-evidence__empty {
  color: var(--th-color-slate-600);
  font-size: 13px;
  line-height: 1.6;
}

.source-only-result header {
  display: grid;
  gap: 6px;
}

.source-only-result header p,
.source-only-result__empty {
  color: var(--th-color-slate-500);
  font-size: 13px;
  line-height: 1.6;
}

.task-evidence dl,
.task-route-info dl,
.manual-review-evidence > dl {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}

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

.task-evidence dt,
.task-route-info dt,
.manual-review-evidence > dl dt {
  color: var(--th-color-slate-500);
  font-size: 12px;
  font-weight: 700;
}

.task-evidence dd,
.task-route-info dd,
.manual-review-evidence > dl dd,
.source-only-result dd {
  margin: 5px 0 0;
  color: var(--th-color-slate-900);
  font-size: 13px;
  overflow-wrap: anywhere;
}

.manual-review-evidence__candidates {
  display: grid;
  gap: 10px;
}

.manual-review-evidence__candidates h3 {
  color: var(--th-color-slate-900);
  font-size: 13px;
}

.candidate-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.candidate-item {
  display: grid;
  gap: 4px;
  border: 1px solid var(--th-color-slate-200);
  border-radius: var(--th-radius-sm);
  background: var(--th-color-white);
  padding: 10px;
}

.candidate-item strong {
  color: var(--th-color-slate-900);
  font-size: 13px;
}

.candidate-item small {
  color: var(--th-color-slate-500);
  font-size: 12px;
}

.candidate-item__attributes {
  display: grid;
  grid-template-columns: 1fr;
  gap: 4px;
  margin: 4px 0 0;
}

.candidate-item__attributes div {
  display: grid;
  grid-template-columns: minmax(90px, 0.35fr) minmax(0, 1fr);
  gap: 8px;
}

.candidate-item__attributes dt,
.candidate-item__attributes dd {
  margin: 0;
  color: var(--th-color-slate-500);
  font-size: 11px;
}

.candidate-item__attributes dd {
  color: var(--th-color-slate-700);
}

.source-only-result__raw {
  grid-column: 1 / -1;
}

.source-only-result pre,
.task-ai-diagnostics pre {
  max-height: 180px;
  overflow: auto;
  border: 1px solid var(--th-color-slate-200);
  border-radius: var(--th-radius-sm);
  background: var(--th-color-white);
  padding: 10px;
  white-space: pre-wrap;
}

.task-ai-diagnostics article {
  display: grid;
  gap: 8px;
  border-top: 1px solid var(--th-color-slate-200);
  padding-top: 10px;
}

.task-ai-diagnostics article:first-of-type {
  border-top: 0;
  padding-top: 0;
}

.task-ai-diagnostics article p {
  margin: 0;
  color: var(--th-color-slate-600);
  font-size: 13px;
}

.task-alert,
.task-notice {
  display: flex;
  align-items: center;
  gap: 8px;
  border-radius: var(--th-radius-sm);
  padding: 10px 12px;
  font-size: 13px;
  line-height: 1.5;
}

.task-alert {
  background: var(--th-color-danger-bg);
  color: var(--th-color-danger);
}

.task-notice {
  background: var(--th-color-success-bg);
  color: var(--th-color-success);
}

.task-detail-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 360px);
  gap: 18px;
  align-items: start;
}

.task-card-main,
.task-side-stack {
  display: grid;
  gap: 16px;
}

.task-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  border-top: 1px solid var(--th-color-slate-200);
  padding-top: 16px;
}

.primary-button,
.secondary-button {
  display: inline-flex;
  min-height: 36px;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: var(--th-radius-sm);
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
}

.primary-button {
  border: 1px solid var(--th-color-blue-600);
  background: var(--th-color-blue-600);
  color: var(--th-color-white);
}

.secondary-button {
  border: 1px solid var(--th-color-slate-200);
  background: var(--th-color-white);
  color: var(--th-color-slate-900);
}

.primary-button:disabled,
.secondary-button:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

@media (max-width: 1040px) {
  .task-detail-body {
    grid-template-columns: 1fr;
  }

  .task-evidence dl,
  .task-route-info dl,
  .manual-review-evidence > dl,
  .candidate-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 640px) {
  .task-evidence dl,
  .task-route-info dl,
  .manual-review-evidence > dl,
  .candidate-grid,
  .source-only-result dl {
    grid-template-columns: 1fr;
  }
}
</style>
