<script setup lang="ts">
import { computed, ref } from 'vue'
import type { BulkResultRegenerationState, PreviewItem, ResultItem } from './types'

const props = defineProps<{
  items: ResultItem[]
  previewItems: PreviewItem[]
  selectedId: string | null
  regeneratingResultId: string | null
  bulkRegeneration: BulkResultRegenerationState
}>()

const emit = defineEmits<{
  'update:selectedId': [value: string]
  'update:field': [id: string, field: 'instruction' | 'input' | 'output', value: string]
  'regenerate:item': [id: string]
  'regenerate:all': []
}>()

const search = ref('')
const invalidOnly = ref(false)
const selectedItem = computed(() => props.items.find((item) => item.id === props.selectedId) ?? props.items[0])
const selectedIndex = computed(() => props.items.findIndex((item) => item.id === selectedItem.value?.id))
const invalidCount = computed(() => (
  props.items.filter((item) => item.savedStatus === 'invalid').length
))
const bulkRegenerationActive = computed(() => props.bulkRegeneration.status === 'running')
const bulkRegenerationVisible = computed(() => (
  props.bulkRegeneration.status !== 'idle' && props.bulkRegeneration.total > 0
))
const bulkRegenerationPercentage = computed(() => (
  props.bulkRegeneration.total > 0
    ? Math.round((props.bulkRegeneration.completed / props.bulkRegeneration.total) * 100)
    : 0
))
const itemRegenerating = (id: string) => (
  props.regeneratingResultId === id
  || props.bulkRegeneration.targetIds.includes(id)
)
const selectedItemRegenerating = computed(() => (
  selectedItem.value ? itemRegenerating(selectedItem.value.id) : false
))
const selectedSource = computed(() => {
  const previewItemId = selectedItem.value?.previewItemId
  if (!previewItemId) return null
  return props.previewItems.find((item) => item.id === previewItemId) ?? null
})
const selectedSourceContent = computed(() => (
  selectedSource.value?.editedContent.trim()
  || selectedSource.value?.originalContent.trim()
  || ''
))
const selectedSourceWasPreprocessed = computed(() => Boolean(
  selectedSource.value
  && selectedSource.value.editedContent.trim()
  && selectedSource.value.editedContent !== selectedSource.value.originalContent
))
const selectedSourceMeta = computed(() => {
  const source = selectedSource.value
  if (!source) return ''
  const parts: string[] = []
  if (source.sourceStartLine != null) {
    parts.push(
      source.sourceEndLine != null && source.sourceEndLine !== source.sourceStartLine
        ? `第 ${source.sourceStartLine}–${source.sourceEndLine} 行`
        : `第 ${source.sourceStartLine} 行`,
    )
  }
  if (source.tokenCount > 0) parts.push(`${source.tokenCount} Token`)
  return parts.join(' · ')
})

const filteredItems = computed(() => props.items.filter((item, index) => {
  const keyword = search.value.trim().toLowerCase()
  const matchesSearch = !keyword
    || item.instruction.toLowerCase().includes(keyword)
    || item.output.toLowerCase().includes(keyword)
    || String(index + 1).includes(keyword)
  return matchesSearch && (!invalidOnly.value || item.status === 'invalid')
}))

function selectRelative(offset: number) {
  if (!props.items.length) return
  const nextIndex = Math.min(Math.max(selectedIndex.value + offset, 0), props.items.length - 1)
  emit('update:selectedId', props.items[nextIndex].id)
}
</script>

<template>
  <section class="result-step">
    <div class="result-workspace">
      <aside class="result-list-pane" :class="{ 'has-bulk-progress': bulkRegenerationVisible }">
        <div class="pane-header result-list-header">
          <div class="result-list-title"><strong>生成结果</strong><span>共 {{ items.length }} 条</span></div>
          <el-button
            v-if="invalidCount > 0"
            size="small"
            plain
            type="primary"
            :loading="bulkRegenerationActive"
            :disabled="Boolean(regeneratingResultId) || bulkRegenerationActive"
            @click="emit('regenerate:all')"
          >
            <i v-if="!bulkRegenerationActive" class="fa fa-refresh" style="margin-right: 4px;" />
            {{ bulkRegenerationActive ? '重新生成中' : `全部重新生成（${invalidCount}）` }}
          </el-button>
        </div>
        <div v-if="bulkRegenerationVisible" class="bulk-regeneration-progress">
          <div>
            <span>已处理 {{ bulkRegeneration.completed }} / {{ bulkRegeneration.total }}</span>
            <span>成功 {{ bulkRegeneration.succeeded }} · 失败 {{ bulkRegeneration.failed }}</span>
          </div>
          <el-progress
            :percentage="bulkRegenerationPercentage"
            :show-text="false"
            :stroke-width="5"
            :color="bulkRegeneration.failed > 0 ? '#d97706' : '#5b50f2'"
          />
        </div>
        <div class="result-toolbar">
          <el-input v-model="search" clearable size="small" placeholder="搜索结果">
            <template #prefix><i class="fa fa-search" /></template>
          </el-input>
          <el-checkbox v-model="invalidOnly">仅看错误</el-checkbox>
        </div>
        <div class="result-list">
          <button
            v-for="item in filteredItems"
            :key="item.id"
            type="button"
            class="result-item"
            :class="{ 'is-active': item.id === selectedItem?.id }"
            @click="emit('update:selectedId', item.id)"
          >
            <span class="result-index">#{{ String(items.findIndex((entry) => entry.id === item.id) + 1).padStart(3, '0') }}</span>
            <span class="result-copy">
              <strong>{{ item.instruction || '未填写指令' }}</strong>
              <small>{{ item.output || '未填写输出' }}</small>
            </span>
            <i v-if="itemRegenerating(item.id)" class="css-spinner" />
            <i
              v-else
              class="fa"
              :class="item.status === 'invalid' ? 'fa-exclamation-circle is-error' : 'fa-check-circle is-valid'"
            />
          </button>
        </div>
      </aside>

      <div v-if="selectedItem" class="result-editor-pane">
        <div class="pane-header">
          <div>
            <strong>结果 #{{ String(selectedIndex + 1).padStart(3, '0') }}</strong>
            <span v-if="selectedItem.status === 'modified'" class="modified-label">已修改</span>
            <el-tag v-if="selectedItem.split" size="small" effect="plain">{{ selectedItem.split }}</el-tag>
            <el-tag
              v-if="selectedItem.qualityScore != null"
              size="small"
              :type="selectedItem.qualityScore >= 80 ? 'success' : selectedItem.qualityScore >= 60 ? 'warning' : 'danger'"
            >质量 {{ selectedItem.qualityScore.toFixed(1) }}</el-tag>
          </div>
          <div class="result-header-actions">
            <el-button
              v-if="selectedItem.savedStatus === 'invalid'"
              size="small"
              plain
              type="primary"
              :loading="selectedItemRegenerating"
              :disabled="bulkRegenerationActive || (Boolean(regeneratingResultId) && !selectedItemRegenerating)"
              @click="emit('regenerate:item', selectedItem.id)"
            >
              <i v-if="!selectedItemRegenerating" class="fa fa-refresh" style="margin-right: 4px;" /> 重新生成
            </el-button>
          </div>
        </div>

        <article class="source-reference" aria-labelledby="source-reference-title">
          <div class="source-reference-heading">
            <div>
              <strong id="source-reference-title">原文参照</strong>
              <span v-if="selectedSourceMeta">{{ selectedSourceMeta }}</span>
            </div>
            <el-tag v-if="selectedSourceWasPreprocessed" size="small" effect="plain">已智能预处理</el-tag>
          </div>
          <pre v-if="selectedSourceContent">{{ selectedSourceContent }}</pre>
          <p v-else>当前结果没有关联到可用的原文切片。</p>
          <small v-if="selectedSourceWasPreprocessed">这里展示的是实际送入模型的预处理后原文，便于核对问题和答案是否有依据。</small>
        </article>

        <div class="field-editor">
          <label>Instruction <em>必填</em></label>
          <el-input
            :model-value="selectedItem.instruction"
            :disabled="selectedItemRegenerating"
            type="textarea"
            :rows="3"
            @update:model-value="emit('update:field', selectedItem.id, 'instruction', $event)"
          />
        </div>
        <div class="field-editor">
          <label>Input <span>选填</span></label>
          <el-input
            :model-value="selectedItem.input"
            :disabled="selectedItemRegenerating"
            type="textarea"
            :rows="2"
            @update:model-value="emit('update:field', selectedItem.id, 'input', $event)"
          />
        </div>
        <div class="field-editor">
          <label>Output <em>必填</em></label>
          <el-input
            :model-value="selectedItem.output"
            :disabled="selectedItemRegenerating"
            type="textarea"
            :rows="7"
            @update:model-value="emit('update:field', selectedItem.id, 'output', $event)"
          />
        </div>
        <div v-if="selectedItem.error" class="validation-error">
          <i class="fa fa-exclamation-circle" /> {{ selectedItem.error }}
        </div>
        <div v-else class="validation-success">
          <i class="fa fa-check-circle" /> 字段校验通过
        </div>
        <div class="editor-pagination">
          <el-button :disabled="selectedIndex <= 0" @click="selectRelative(-1)">上一条</el-button>
          <span>{{ selectedIndex + 1 }} / {{ items.length }}</span>
          <el-button :disabled="selectedIndex >= items.length - 1" @click="selectRelative(1)">下一条</el-button>
        </div>
      </div>
    </div>
  </section>
</template>

<style scoped lang="scss">
.result-step {
  min-width: 0;
}


.result-workspace {
  display: grid;
  grid-template-columns: minmax(280px, 34fr) minmax(480px, 66fr);
  min-height: clamp(420px, calc(100vh - 500px), 590px);
  overflow: hidden;
  border: 1px solid #e2e5ec;
  border-radius: 9px;
}

.result-list-pane {
  min-width: 0;
  border-right: 1px solid #e5e8ee;
}

.result-list-title {
  min-width: 0;

  span {
    margin-left: 8px;
    color: #8a93a3;
    font-size: 11px;
    font-weight: 400;
  }
}

.result-list-header {
  gap: 10px;

  :deep(.el-button) {
    flex: none;
  }
}

.pane-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 52px;
  padding: 0 15px;
  color: #344054;
  background: #fff;
  border-bottom: 1px solid #e8ebf0;
  font-size: 13px;

  > div {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  > span,
  .modified-label {
    color: #8a93a3;
    font-size: 11px;
  }
}

.result-toolbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border-bottom: 1px solid #edf0f5;
}

.bulk-regeneration-progress {
  padding: 9px 12px 10px;
  background: #fafaff;
  border-bottom: 1px solid #e8e7ff;

  > div {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 7px;
    color: #667085;
    font-size: 10px;
  }
}

.result-list {
  height: 476px;
  padding: 7px;
  overflow: auto;
}

.has-bulk-progress .result-list {
  height: 420px;
}

.result-header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.result-item {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr) 18px;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 62px;
  padding: 9px;
  text-align: left;
  background: #fff;
  border: 1px solid transparent;
  border-bottom-color: #edf0f5;
  cursor: pointer;

  &:hover,
  &.is-active {
    background: #f7f6ff;
  }

  &.is-active {
    border-color: #5b50f2;
    border-radius: 6px;
  }
}

.result-index {
  color: #667085;
  font-size: 11px;
}

.result-copy {
  min-width: 0;

  strong,
  small {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  strong {
    color: #344054;
    font-size: 12px;
  }

  small {
    margin-top: 5px;
    color: #98a2b3;
    font-size: 10px;
  }
}

.is-valid {
  color: #2ca66a;
}

.is-error {
  color: #d97706;
}

.css-spinner {
  width: 14px;
  height: 14px;
  border: 2px solid rgba(91, 80, 242, 0.2);
  border-top-color: #5b50f2;
  border-radius: 50%;
  animation: css-spin 0.8s linear infinite;
  display: inline-block;
}

@keyframes css-spin {
  to { transform: rotate(360deg); }
}

.result-editor-pane {
  min-width: 0;
}

.source-reference {
  margin: 14px 18px 2px;
  padding: 12px 14px;
  border: 1px solid #dfe4ec;
  border-radius: 7px;
  background: #f8fafc;

  pre {
    max-height: 168px;
    margin: 10px 0 0;
    overflow: auto;
    color: #344054;
    font-family: inherit;
    font-size: 12px;
    line-height: 1.7;
    white-space: pre-wrap;
    word-break: break-word;
  }

  p {
    margin: 10px 0 0;
    color: #98a2b3;
    font-size: 12px;
  }

  > small {
    display: block;
    margin-top: 8px;
    color: #8a93a3;
    font-size: 10px;
  }
}

.source-reference-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;

  > div {
    display: flex;
    align-items: baseline;
    gap: 8px;
  }

  strong {
    color: #344054;
    font-size: 12px;
  }

  span {
    color: #8a93a3;
    font-size: 10px;
  }
}

.field-editor {
  padding: 13px 18px 0;

  label {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 7px;
    color: #344054;
    font-size: 12px;
    font-weight: 650;
  }

  em {
    color: #e05252;
    font-size: 10px;
    font-style: normal;
    font-weight: 400;
  }

  span {
    color: #98a2b3;
    font-size: 10px;
    font-weight: 400;
  }
}

.validation-error,
.validation-success {
  margin: 12px 18px 0;
  padding: 9px 11px;
  font-size: 11px;
  border-radius: 6px;
}

.validation-error {
  color: #b45309;
  background: #fff7e8;
}

.validation-success {
  color: #25895c;
  background: #edf9f3;
}

.editor-pagination {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  padding: 12px 18px;

  span {
    color: #8a93a3;
    font-size: 11px;
  }
}

@media (max-width: 900px) {
  .result-workspace {
    grid-template-columns: minmax(0, 1fr);
  }

  .result-list-pane {
    border-right: 0;
    border-bottom: 1px solid #e5e8ee;
  }

  .result-list {
    height: 260px;
  }
}
</style>
