<script setup lang="ts">
import type {ChatThinkingBlockProps} from './types';

import {computed, ref, watch} from 'vue';

import {ChatEventLabel} from '../chat-status';

defineOptions({
  name: 'ChatThinkingBlock',
});

const props = withDefaults(defineProps<ChatThinkingBlockProps>(), {
  content: '',
  disabled: false,
  emptyBehavior: 'hide',
  expanded: false,
  label: '',
  readonly: false,
  status: 'end',
  summary: '',
});

const emit = defineEmits<{
  'update:expanded': [boolean];
}>();

const innerExpanded = ref(props.expanded);

watch(
  () => props.expanded,
  (value) => {
    innerExpanded.value = value;
  },
);

const normalizedContent = computed(() =>
  String(props.content || '')
    .replaceAll('\r\n', '\n')
    .replace(/^\s*\n/, '')
    .trimEnd(),
);

const shouldRender = computed(
  () =>
    normalizedContent.value.length > 0 || props.emptyBehavior === 'placeholder',
);

const expandedModel = computed({
  get: () => innerExpanded.value,
  set: (value: boolean) => {
    innerExpanded.value = value;
    emit('update:expanded', value);
  },
});

const computedLabel = computed(() => {
  if (props.label) {
    return props.label;
  }
  if (props.status === 'thinking') {
    return '思考中';
  }
  if (props.status === 'error') {
    return '思考异常';
  }
  return '已思考';
});

const canToggle = computed(
  () => !props.disabled && normalizedContent.value.length > 0,
);

function toggleExpanded() {
  if (!canToggle.value) {
    return;
  }
  expandedModel.value = !expandedModel.value;
}
</script>

<template>
  <div
    v-if="shouldRender"
    class="chat-thinking-block"
    :class="[
      `is-${status}`,
      {
        'is-disabled': disabled,
        'is-expanded': expandedModel,
        'is-readonly': readonly,
      },
    ]"
  >
    <button
      type="button"
      class="chat-thinking-block__trigger"
      :aria-expanded="expandedModel"
      :disabled="!canToggle"
      @click="toggleExpanded"
    >
      <ChatEventLabel
        class="chat-thinking-block__label"
        :active="status === 'thinking'"
        :text="computedLabel"
      />

      <span
        class="chat-thinking-block__chevron"
        :class="{ 'is-open': expandedModel }"
        aria-hidden="true"
      ></span>
    </button>

    <transition name="chat-thinking-block__body-transition">
      <div
        v-if="expandedModel && normalizedContent"
        class="chat-thinking-block__body"
      >
        <span class="chat-thinking-block__rail" aria-hidden="true"></span>
        <div class="chat-thinking-block__content">{{ normalizedContent }}</div>
      </div>
    </transition>
  </div>
</template>

<style scoped>
.chat-thinking-block {
  max-width: 100%;
  color: var(--el-text-color-placeholder);
}

.chat-thinking-block__trigger {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  max-width: 100%;
  min-width: 0;
  padding: 2px 0;
  color: inherit;
  text-align: left;
  background: transparent;
  border: 0;
  border-radius: 6px;
  transition:
    color 0.18s ease,
    opacity 0.18s ease;
}

.chat-thinking-block__trigger:not(:disabled) {
  cursor: pointer;
}

.chat-thinking-block__trigger:not(:disabled):hover {
  color: var(--el-text-color-secondary);
}

.chat-thinking-block__trigger:focus-visible {
  outline: 2px solid var(--el-color-primary-light-5);
  outline-offset: 3px;
}

.chat-thinking-block__trigger:disabled {
  cursor: default;
}

.chat-thinking-block__label {
  max-width: 100%;
}

.chat-thinking-block__chevron {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  margin-left: 2px;
  border-right: 1.5px solid var(--el-text-color-secondary);
  border-bottom: 1.5px solid var(--el-text-color-secondary);
  transform: rotate(45deg) translateY(-2px);
  transition:
    border-color 0.18s ease,
    transform 0.18s ease;
}

.chat-thinking-block__chevron.is-open {
  transform: rotate(225deg) translate(-2px, -1px);
}

.chat-thinking-block__body {
  position: relative;
  display: grid;
  grid-template-columns: 13px minmax(0, 1fr);
  column-gap: 10px;
  padding: 8px 0 0;
  margin-left: 3px;
}

.chat-thinking-block__rail {
  position: relative;
  min-height: 100%;
}

.chat-thinking-block__rail::before {
  position: absolute;
  top: 4px;
  bottom: 2px;
  left: 3px;
  width: 1px;
  content: '';
  background: var(--el-border-color);
}

.chat-thinking-block__rail::after {
  position: absolute;
  top: 0;
  left: 0;
  width: 7px;
  height: 7px;
  content: '';
  background: var(--el-text-color-placeholder);
  border-radius: 999px;
}

.chat-thinking-block.is-thinking .chat-thinking-block__rail::after {
  background: var(--el-color-primary);
  box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
  animation: chat-thinking-pulse 1.8s ease-in-out infinite;
}

.chat-thinking-block.is-error .chat-thinking-block__rail::after {
  background: var(--el-color-danger);
  box-shadow: 0 0 0 4px var(--el-color-danger-light-9);
}

.chat-thinking-block__content {
  min-width: 0;
  padding: 0 0 2px;
  margin: 0;
  font-size: 14px;
  line-height: 24px;
  color: var(--el-text-color-secondary);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.chat-thinking-block.is-disabled {
  opacity: 0.82;
}

.chat-thinking-block__body-transition-enter-active,
.chat-thinking-block__body-transition-leave-active {
  transition:
    opacity 0.18s ease,
    transform 0.18s ease;
}

.chat-thinking-block__body-transition-enter-from,
.chat-thinking-block__body-transition-leave-to {
  opacity: 0;
  transform: translateY(-4px);
}

@keyframes chat-thinking-pulse {
  0%,
  100% {
    opacity: 0.86;
  }

  50% {
    opacity: 0.92;
  }
}

@keyframes chat-thinking-shimmer {
  0% {
    background-position: 120% 0;
  }

  100% {
    background-position: -120% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .chat-thinking-block.is-thinking .chat-thinking-block__rail::after {
    animation: none;
  }
}

@media (width <= 768px) {
  .chat-thinking-block__content {
    font-size: 13px;
    line-height: 22px;
  }
}
</style>
