<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue'
import { sourceLines } from './previewModel'
import type { PreviewItem, ProcessType } from './types'

const props = defineProps<{
  sourceText: string
  items: PreviewItem[]
  selectedId: string | null
  processType: ProcessType
  fileName: string
  files: { id: string; name: string; count: number; modifiedCount: number }[]
  selectedFileId: string | null
}>()

const emit = defineEmits<{
  'update:selectedId': [value: string]
  'update:selectedFileId': [value: string]
  'update:item-content': [id: string, value: string]
  'restore:item': [id: string]
  'add:item': []
  'remove:item': [id: string]
}>()

const sourceViewerRef = ref<HTMLElement | null>(null)
const search = ref('')
const currentPage = ref(1)
const PREVIEW_PAGE_SIZE = 6
const editingItemId = ref<string | null>(null)
const editorDraft = ref('')
const lines = computed(() => sourceLines(props.sourceText))
const selectedItem = computed(() => props.items.find((item) => item.id === props.selectedId) ?? props.items[0])
const editingItem = computed(() => props.items.find((item) => item.id === editingItemId.value))

const filteredItems = computed(() => props.items.filter((item, index) => {
  const matchesSearch = !search.value.trim()
    || item.editedContent.toLowerCase().includes(search.value.trim().toLowerCase())
    || String(index + 1).includes(search.value.trim())
  return matchesSearch
}))

const pagedItems = computed(() => {
  const start = (currentPage.value - 1) * PREVIEW_PAGE_SIZE
  return filteredItems.value.slice(start, start + PREVIEW_PAGE_SIZE)
})

const selectedIndex = computed(() => props.items.findIndex((item) => item.id === selectedItem.value?.id))

function isLineHighlighted(lineStart: number, lineEnd: number) {
  const item = selectedItem.value
  if (!item || item.sourceStart == null || item.sourceEnd == null) return false
  return lineEnd >= item.sourceStart && lineStart <= item.sourceEnd
}

function selectItem(id: string) {
  emit('update:selectedId', id)
}

function openEditor(item: PreviewItem) {
  selectItem(item.id)
  editingItemId.value = item.id
  editorDraft.value = item.editedContent
}

function closeEditor() {
  editingItemId.value = null
  editorDraft.value = ''
}

function saveEditor() {
  if (!editingItem.value) return
  emit('update:item-content', editingItem.value.id, editorDraft.value)
  closeEditor()
}

function restoreItem() {
  if (!editingItem.value) return
  emit('restore:item', editingItem.value.id)
  closeEditor()
}

function removeItem(item: PreviewItem) {
  selectItem(item.id)
  emit('remove:item', item.id)
}

function handlePageChange() {
  closeEditor()
}

watch(search, () => {
  currentPage.value = 1
  closeEditor()
})

watch(() => props.selectedFileId, closeEditor)

watch(selectedItem, async (item) => {
  if (!item) return
  const visibleIndex = filteredItems.value.findIndex((entry) => entry.id === item.id)
  if (visibleIndex >= 0) {
    currentPage.value = Math.floor(visibleIndex / PREVIEW_PAGE_SIZE) + 1
  }

  if (item.sourceStart == null) return
  await nextTick()
  const target = sourceViewerRef.value?.querySelector<HTMLElement>(`[data-source-start="${item.sourceStart}"]`)
    ?? sourceViewerRef.value?.querySelector<HTMLElement>('.source-line.is-highlighted')
  target?.scrollIntoView({ block: 'center', behavior: 'smooth' })
}, { immediate: true })

function itemNumber(item: PreviewItem) {
  return props.items.findIndex((entry) => entry.id === item.id) + 1
}

function lineRange(item: PreviewItem) {
  if (item.sourceStartLine == null || item.sourceEndLine == null) return '手动新增，无源文件定位'
  return item.sourceStartLine === item.sourceEndLine
    ? `来源：第 ${item.sourceStartLine} 行`
    : `来源：第 ${item.sourceStartLine}–${item.sourceEndLine} 行`
}
</script>

<template>
  <section class="preview-step">
    <div class="preview-file-switcher">
      <div class="file-switcher-control">
        <span>当前文件</span>
        <el-select
          :model-value="selectedFileId"
          filterable
          placeholder="选择文件"
          aria-label="选择当前预览文件"
          @update:model-value="emit('update:selectedFileId', $event)"
        >
          <el-option
            v-for="file in files"
            :key="file.id"
            :label="file.name"
            :value="file.id"
          >
            <div class="file-option">
              <strong :title="file.name">{{ file.name }}</strong>
              <span>{{ file.count.toLocaleString() }} {{ processType === 'unstructured' ? '个切片' : '条记录' }}</span>
              <em v-if="file.modifiedCount">{{ file.modifiedCount }} 处已修改</em>
            </div>
          </el-option>
        </el-select>
      </div>
      <span class="file-switcher-summary">
        {{ files.length }} 个文件 · 当前文件 {{ items.length.toLocaleString() }} {{ processType === 'unstructured' ? '个切片' : '条记录' }}
      </span>
    </div>

    <div class="preview-workspace">
      <div class="source-pane">
        <div class="pane-header">
          <div>
            <strong>源文件 · {{ fileName }}</strong>
          </div>
        </div>

        <div ref="sourceViewerRef" class="source-viewer" tabindex="0" aria-label="源文件内容">
          <div
            v-for="line in lines"
            :key="line.number"
            class="source-line"
            :class="{ 'is-highlighted': isLineHighlighted(line.start, line.end) }"
            :data-source-start="line.start"
          >
            <span class="line-number">{{ line.number }}</span>
            <span class="line-content">{{ line.content || ' ' }}</span>
          </div>
        </div>
      </div>

      <div class="preview-pane">
        <div class="pane-header">
          <strong>{{ processType === 'unstructured' ? '切片内容' : '记录内容' }}</strong>
          <div class="pane-header-actions">
            <span>共 {{ items.length.toLocaleString() }} 条</span>
            <el-button link type="primary" @click="emit('add:item')">
              <i class="fa fa-plus" /> 手动新增
            </el-button>
          </div>
        </div>

        <template v-if="!editingItem">
          <div class="preview-toolbar">
            <el-input v-model="search" clearable placeholder="搜索编号或内容" size="small">
              <template #prefix><i class="fa fa-search" /></template>
            </el-input>
          </div>

          <div class="preview-list" aria-label="预览条目列表">
            <div
              v-for="item in pagedItems"
              :key="item.id"
              class="preview-item"
              :class="{ 'is-active': item.id === selectedItem?.id }"
              role="button"
              tabindex="0"
              @click="selectItem(item.id)"
              @keydown.enter="selectItem(item.id)"
              @keydown.space.prevent="selectItem(item.id)"
            >
              <span class="item-name">{{ processType === 'unstructured' ? '切片' : '记录' }} #{{ String(itemNumber(item)).padStart(3, '0') }}</span>
              <span class="item-source">{{ lineRange(item) }}</span>
              <span class="item-actions">
                <el-button link aria-label="编辑切片" title="编辑" @click.stop="openEditor(item)">
                  <i class="fa fa-pencil" />
                </el-button>
                <el-button link type="danger" aria-label="删除切片" title="删除" @click.stop="removeItem(item)">
                  <i class="fa fa-trash-o" />
                </el-button>
              </span>
            </div>
            <div v-if="!filteredItems.length" class="empty-result">没有符合条件的内容</div>
          </div>

          <el-pagination
            v-if="filteredItems.length > PREVIEW_PAGE_SIZE"
            v-model:current-page="currentPage"
            :page-size="PREVIEW_PAGE_SIZE"
            :total="filteredItems.length"
            :pager-count="5"
            small
            background
            layout="prev, pager, next"
            class="preview-pagination"
            @current-change="handlePageChange"
          />
        </template>

        <template v-else>
          <div class="preview-editor">
          <div class="editor-heading">
            <div>
              <strong>{{ processType === 'unstructured' ? '切片' : '记录' }} #{{ String(itemNumber(editingItem)).padStart(3, '0') }} 正文</strong>
              <small>{{ lineRange(editingItem) }}</small>
            </div>
          </div>
          <el-input
            v-model="editorDraft"
            type="textarea"
            :rows="7"
            resize="none"
          />
          <div class="editor-actions">
            <el-button
              v-if="editingItem.sourceStart != null"
              link
              @click="restoreItem"
            >
              <i class="fa fa-undo" /> 恢复原始内容
            </el-button>
            <div>
              <el-button @click="closeEditor">取消</el-button>
              <el-button type="primary" @click="saveEditor">保存修改</el-button>
            </div>
          </div>
          </div>
        </template>
      </div>
    </div>
  </section>
</template>

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

.preview-file-switcher {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 58px;
  padding: 10px 14px;
  margin-bottom: 12px;
  background: #fff;
  border: 1px solid #e2e5ec;
  border-radius: 9px;
}

.file-switcher-control {
  display: flex;
  align-items: center;
  min-width: 0;
  gap: 10px;

  > span {
    flex: none;
    color: #667085;
    font-size: 12px;
  }

  :deep(.el-select) {
    width: min(360px, 42vw);
  }
}

.file-switcher-summary {
  color: #7d8798;
  font-size: 12px;
  text-align: right;
  white-space: nowrap;
}

.pane-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;

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

.file-option {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 12px;
  max-width: 440px;

  strong,
  span,
  em {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  strong {
    color: #344054;
    font-size: 13px;
    font-style: normal;
  }

  span {
    color: #98a2b3;
    font-size: 11px;
  }

  em {
    color: #5549dc;
    font-size: 11px;
    font-style: normal;
  }
}

.preview-workspace {
  display: grid;
  grid-template-columns: minmax(0, 58fr) minmax(380px, 42fr);
  height: clamp(560px, calc(100vh - 370px), 720px);
  overflow: hidden;
  border: 1px solid #e2e5ec;
  border-radius: 9px;
}

.source-pane,
.preview-pane {
  display: flex;
  min-width: 0;
  min-height: 0;
  flex-direction: column;
  background: #fff;
}

.source-pane {
  border-right: 1px solid #e5e8ee;
}

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

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

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

.source-viewer {
  flex: 1;
  height: 538px;
  padding: 12px 0 24px;
  overflow: auto;
  outline: none;
  background: #fff;
  scroll-behavior: smooth;
}

.source-line {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr);
  min-height: 29px;
  color: #424b5d;
  font-size: 12px;
  line-height: 1.8;
  border-left: 3px solid transparent;
  transition: background-color 0.18s ease, border-color 0.18s ease;

  &.is-highlighted {
    background: #eeedff;
    border-left-color: #5b50f2;
  }
}

.line-number {
  padding-right: 11px;
  color: #a0a7b4;
  text-align: right;
  user-select: none;
}

.line-content {
  min-width: 0;
  padding: 2px 14px 2px 0;
  white-space: pre-wrap;
  word-break: break-word;
}

.preview-toolbar {
  padding: 10px 12px;
  border-bottom: 1px solid #edf0f5;
}

.preview-list {
  flex: 1 1 auto;
  min-height: 0;
  padding: 8px;
  overflow: auto;
  border-bottom: 1px solid #e8ebf0;
}

.preview-pagination {
  display: flex;
  justify-content: flex-end;
  min-height: 38px;
  padding: 6px 12px;
  border-bottom: 1px solid #e8ebf0;
}

.preview-item {
  display: grid;
  grid-template-columns: 94px minmax(130px, 1fr) auto;
  align-items: center;
  width: 100%;
  min-height: 40px;
  padding: 0 10px;
  color: #6b7382;
  text-align: left;
  background: #fff;
  border: 1px solid transparent;
  border-bottom-color: #edf0f5;
  cursor: pointer;

  &:hover {
    background: #fafaff;
  }

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

.item-name {
  color: #344054;
  font-size: 12px;
  font-weight: 650;
}

.item-source,
.item-actions {
  overflow: hidden;
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.item-actions {
  display: flex;
  align-items: center;
  gap: 2px;

  :deep(.el-button) {
    width: 28px;
    min-height: 28px;
    padding: 0;
    margin: 0;
  }
}

.empty-result {
  padding: 34px 16px;
  color: #98a2b3;
  text-align: center;
  font-size: 12px;
}

.preview-editor {
  display: flex;
  min-height: 0;
  flex: 1 1 auto;
  flex-direction: column;
  padding: 13px;
  overflow-y: auto;
}

.editor-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;

  strong,
  small {
    display: block;
  }

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

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

.preview-editor :deep(.el-textarea__inner) {
  min-height: 260px !important;
  color: #3f4756;
  font-size: 12px;
  line-height: 1.75;
}

.editor-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  margin-top: auto;
  padding-top: 8px;
}

@media (max-width: 1100px) {
  .preview-workspace {
    grid-template-columns: minmax(0, 52fr) minmax(360px, 48fr);
  }

  .preview-item {
    grid-template-columns: 88px minmax(0, 1fr) auto;
  }
}

@media (max-width: 900px) {
  .preview-file-switcher {
    align-items: flex-start;
    flex-direction: column;
    gap: 8px;
  }

  .file-switcher-control {
    width: 100%;

    :deep(.el-select) {
      flex: 1;
      width: auto;
    }
  }

  .file-switcher-summary {
    text-align: left;
    white-space: normal;
  }

  .preview-workspace {
    grid-template-columns: minmax(0, 1fr);
    height: auto;
  }

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

  .source-viewer {
    height: 320px;
    flex: none;
  }
}
</style>
