<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import type { UploadFile } from 'element-plus'
import type { ExternalDataSource, ProcessType, UploadedDataFile } from './types'

const props = defineProps<{
  processType: ProcessType
  uploadedFiles: UploadedDataFile[]
  externalSource: ExternalDataSource
  externalPulling: boolean
  externalConnected: boolean
}>()

const emit = defineEmits<{
  'update:externalSource': [value: ExternalDataSource]
  'file-change': [file: UploadFile]
  'remove-file': [uid: string | number]
  'use-sample': []
  'test-connection': []
  'pull-data': []
}>()

const DATA_SOURCE_TYPES = [
  { value: 'postgresql', label: 'PostgreSQL' },
]

const AUTH_MODES = [
  { value: 'none', label: '免鉴权' },
  { value: 'basic', label: '账号密码' },
]

const FILE_PAGE_SIZE = 10
const currentFilePage = ref(1)

const isExternal = computed(() => props.processType === 'external')

// 后端首版严格支持这些可验证的文本格式；不要把无法解析的二进制文档
// 静默替换成示例正文。
const uploadAccept = computed(() => props.processType === 'unstructured'
  ? '.txt,.md,.json,.jsonl'
  : '.json,.jsonl,.csv,.txt,.md')

const pagedUploadedFiles = computed(() => {
  const start = (currentFilePage.value - 1) * FILE_PAGE_SIZE
  return props.uploadedFiles.slice(start, start + FILE_PAGE_SIZE)
})

watch(() => props.uploadedFiles.length, (newLength, oldLength) => {
  const totalPages = Math.max(1, Math.ceil(newLength / FILE_PAGE_SIZE))
  if (newLength > oldLength) {
    currentFilePage.value = totalPages
    return
  }
  currentFilePage.value = Math.min(currentFilePage.value, totalPages)
})

watch(() => props.processType, () => {
  currentFilePage.value = 1
})

function updateExternalField<K extends keyof ExternalDataSource>(field: K, value: ExternalDataSource[K]) {
  emit('update:externalSource', { ...props.externalSource, [field]: value })
}

function formatSize(size: number) {
  if (!size) return '0 KB'
  if (size >= 1024 * 1024) return `${(size / 1024 / 1024).toFixed(1)} MB`
  return `${(size / 1024).toFixed(1)} KB`
}
</script>

<template>
  <section class="source-upload-step" aria-labelledby="source-upload-title">
    <div v-if="isExternal" class="form-section external-section">
      <div class="section-title-row">
        <div>
          <h3 id="source-upload-title">数据源配置</h3>
          <p>配置并验证外部数据源，拉取成功后可在下一步预览数据内容</p>
        </div>
      </div>

      <div class="external-form">
        <el-form label-position="top" class="external-grid">
          <el-form-item label="数据源类型">
            <el-select
              :model-value="externalSource.type"
              placeholder="请选择数据源类型"
              aria-label="数据源类型"
              @update:model-value="updateExternalField('type', $event)"
            >
              <el-option
                v-for="item in DATA_SOURCE_TYPES"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="地址 / URL">
            <el-input
              :model-value="externalSource.url"
              placeholder="例如：postgresql://db.example.com:5432/my_database"
              aria-label="数据源地址或 URL"
              @update:model-value="updateExternalField('url', $event)"
            />
          </el-form-item>
          <el-form-item label="鉴权方式">
            <el-select
              :model-value="externalSource.authMode"
              aria-label="鉴权方式"
              @update:model-value="updateExternalField('authMode', $event)"
            >
              <el-option
                v-for="item in AUTH_MODES"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item v-if="externalSource.authMode === 'basic'" label="账号">
            <el-input
              :model-value="externalSource.username"
              autocomplete="username"
              placeholder="请输入账号"
              aria-label="数据源账号"
              @update:model-value="updateExternalField('username', $event)"
            />
          </el-form-item>
          <el-form-item v-if="externalSource.authMode === 'basic'" label="密码">
            <el-input
              :model-value="externalSource.password"
              type="password"
              show-password
              autocomplete="current-password"
              placeholder="请输入密码"
              aria-label="数据源密码"
              @update:model-value="updateExternalField('password', $event)"
            />
          </el-form-item>
          <el-form-item label="拉取条数">
            <el-input-number
              :model-value="externalSource.limit"
              :min="1"
              :max="100000"
              :step="100"
              controls-position="right"
              aria-label="数据拉取条数"
              @update:model-value="updateExternalField('limit', Number($event) || 0)"
            />
          </el-form-item>
          <el-form-item label="只读查询语句" class="external-query-field">
            <el-input
              :model-value="externalSource.query"
              type="textarea"
              :rows="4"
              maxlength="20000"
              show-word-limit
              placeholder="例如：SELECT question, answer FROM qa_data ORDER BY id"
              aria-label="外部数据源只读查询语句"
              @update:model-value="updateExternalField('query', $event)"
            />
            <small>只允许单条 SELECT 或 WITH 查询；后端会拒绝写入、DDL 和多语句。</small>
          </el-form-item>
        </el-form>

        <div class="external-actions">
          <el-button
            :loading="externalPulling && !externalConnected"
            :disabled="externalPulling"
            plain
            @click="emit('test-connection')"
          >
            测试连接
          </el-button>
          <el-button
            type="primary"
            :loading="externalPulling"
            :disabled="externalPulling"
            @click="emit('pull-data')"
          >
            拉取数据
          </el-button>
          <span v-if="externalConnected" class="external-status is-connected" role="status">
            <i class="fa fa-check-circle" aria-hidden="true" /> 连接正常
          </span>
        </div>

        <section v-if="uploadedFiles.length" class="uploaded-file-list" aria-label="已拉取数据列表">
          <div class="uploaded-file-list-header">
            <span>已拉取 {{ uploadedFiles.length }} 个数据集</span>
          </div>
          <div class="uploaded-file-items">
            <div v-for="file in pagedUploadedFiles" :key="file.uid" class="uploaded-file">
              <span class="file-icon"><i class="fa fa-cloud-download" aria-hidden="true" /></span>
              <div class="file-main">
                <strong :title="file.name">{{ file.name }}</strong>
                <span>
                  {{ formatSize(file.size) }}
                  <template v-if="file.count"> · {{ file.count.toLocaleString() }} 条</template>
                </span>
              </div>
              <span class="file-status"><i class="fa fa-check-circle" aria-hidden="true" /> 拉取成功</span>
              <el-button
                link
                type="danger"
                :aria-label="`删除数据集 ${file.name}`"
                @click="emit('remove-file', file.uid)"
              >
                删除
              </el-button>
            </div>
          </div>
          <el-pagination
            v-if="uploadedFiles.length > FILE_PAGE_SIZE"
            v-model:current-page="currentFilePage"
            :page-size="FILE_PAGE_SIZE"
            :total="uploadedFiles.length"
            :pager-count="5"
            small
            background
            layout="prev, pager, next"
            class="uploaded-file-pagination"
            aria-label="已拉取数据分页"
          />
        </section>
      </div>
    </div>

    <div v-else class="form-section upload-section">
      <div class="section-title-row">
        <div>
          <h3 id="source-upload-title">源数据上传</h3>
          <p>上传后可在下一步检查内容和切分效果，支持同时添加多个文件</p>
        </div>
        <el-button v-if="uploadedFiles.length === 0" link type="primary" @click="emit('use-sample')">
          使用示例数据
        </el-button>
      </div>

      <el-upload
        v-if="uploadedFiles.length === 0"
        drag
        multiple
        :accept="uploadAccept"
        :auto-upload="false"
        :show-file-list="false"
        :on-change="(file: UploadFile) => emit('file-change', file)"
        aria-label="选择或拖拽源数据文件"
      >
        <i class="fa fa-cloud-upload upload-icon" aria-hidden="true" />
        <div class="el-upload__text">拖拽文件到此处，或<em>点击选择文件</em></div>
        <template #tip>
          <div class="el-upload__tip">
            {{ processType === 'unstructured'
              ? '支持 TXT、Markdown、PDF、Word、JSON、JSONL，单文件不超过 200MB'
              : '支持 JSON、JSONL、CSV、Excel，单文件不超过 200MB' }}
          </div>
        </template>
      </el-upload>

      <section v-else class="uploaded-file-list" aria-label="已上传文件列表">
        <div class="uploaded-file-list-header">
          <span>已添加 {{ uploadedFiles.length }} 个文件</span>
          <div class="continue-upload">
            <el-upload
              multiple
              :accept="uploadAccept"
              :auto-upload="false"
              :show-file-list="false"
              :on-change="(file: UploadFile) => emit('file-change', file)"
              aria-label="继续添加源数据文件"
            >
              <el-button size="small" type="primary">继续上传</el-button>
            </el-upload>
          </div>
        </div>
        <div class="uploaded-file-items">
          <div v-for="file in pagedUploadedFiles" :key="file.uid" class="uploaded-file">
            <span class="file-icon"><i class="fa fa-file-text-o" aria-hidden="true" /></span>
            <div class="file-main">
              <strong :title="file.name">{{ file.name }}</strong>
              <span>
                {{ formatSize(file.size) }}
                <template v-if="file.count"> · {{ file.count.toLocaleString() }} 条</template>
              </span>
            </div>
            <span class="file-status"><i class="fa fa-check-circle" aria-hidden="true" /> 校验通过</span>
            <el-button
              link
              type="danger"
              :aria-label="`删除文件 ${file.name}`"
              @click="emit('remove-file', file.uid)"
            >
              删除
            </el-button>
          </div>
        </div>
        <el-pagination
          v-if="uploadedFiles.length > FILE_PAGE_SIZE"
          v-model:current-page="currentFilePage"
          :page-size="FILE_PAGE_SIZE"
          :total="uploadedFiles.length"
          :pager-count="5"
          small
          background
          layout="prev, pager, next"
          class="uploaded-file-pagination"
          aria-label="已上传文件分页"
        />
      </section>
    </div>
  </section>
</template>

<style scoped lang="scss">
.source-upload-step {
  width: 100%;
}

.form-section {
  padding: 0;

  h3 {
    margin: 0 0 5px;
    color: #2f3747;
    font-size: 15px;
    font-weight: 650;
  }
}

.section-title-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;

  p {
    margin: 0;
    color: #8a93a3;
    font-size: 12px;
    line-height: 1.6;
  }
}

.external-section {
  .external-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px 20px;
    margin-top: 16px;
  }

  :deep(.el-form-item) {
    margin-bottom: 0;
  }

  :deep(.el-select),
  :deep(.el-input),
  :deep(.el-input-number) {
    width: 100%;
  }
}

.external-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 22px;
}

.external-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;

  &.is-connected {
    color: #2ca66a;
  }
}

.upload-section :deep(.el-upload) {
  width: 100%;
  margin-top: 16px;
}

.upload-section :deep(.el-upload-dragger) {
  width: 100%;
  min-height: 154px;
  padding: 32px 20px;
  background: #fff;
  border-color: #dfe3ea;
  transition: border-color 0.18s ease, background-color 0.18s ease;

  &:hover,
  &:focus-visible {
    background: #fafaff;
    border-color: #8b82f4;
  }

  &:focus-visible {
    outline: 2px solid #5b50f2;
    outline-offset: 2px;
  }
}

.upload-icon {
  margin-bottom: 12px;
  color: #5b50f2;
  font-size: 30px;
}

.uploaded-file-list {
  margin-top: 20px;
  overflow: hidden;
  background: #fff;
  border: 1px solid #dfe3ea;
  border-radius: 8px;
}

.uploaded-file-list-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  color: #5f6878;
  font-size: 12px;
  background: #fff;
  border-bottom: 1px solid #edf0f5;
}

.continue-upload {
  flex: 0 0 auto;
}

.continue-upload :deep(.el-upload) {
  width: auto;
  margin-top: 0;
}

.uploaded-file-pagination {
  display: flex;
  justify-content: flex-end;
  padding: 10px 14px;
  border-top: 1px solid #edf0f5;
}

.uploaded-file {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 8px 14px;
  border-bottom: 1px solid #edf0f5;

  &:last-child {
    border-bottom: 0;
  }
}

.file-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  color: #5b50f2;
  font-size: 14px;
  background: #f0efff;
  border-radius: 7px;
}

.file-main {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 5px;
  min-width: 0;

  strong {
    overflow: hidden;
    color: #273142;
    font-size: 14px;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

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

.file-status {
  color: #2ca66a;
  font-size: 12px;
}

.uploaded-file :deep(.el-button) {
  flex: 0 0 auto;
}

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

  .uploaded-file {
    gap: 8px;
    padding: 8px 10px;
  }

  .file-status {
    flex: 0 1 auto;
    line-height: 1.4;
    white-space: normal;
  }

  .uploaded-file-pagination {
    justify-content: center;
  }
}

@media (max-width: 560px) {
  .section-title-row {
    align-items: flex-start;
    flex-direction: column;
  }

  .uploaded-file-list-header {
    align-items: flex-start;
    flex-direction: column;
  }

  .uploaded-file {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .file-main {
    min-width: calc(100% - 40px);
  }

  .file-status {
    margin-left: 38px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .upload-section :deep(.el-upload-dragger) {
    transition: none;
  }
}
</style>
