<script lang="ts" setup>
import { computed, onMounted, reactive, ref } from 'vue';

import { Page } from '@vben/common-ui';

import {
  Button,
  Card,
  Col,
  DatePicker,
  Empty,
  Input,
  message,
  Modal,
  Row,
  Select,
  Space,
  Steps,
  Tag,
} from 'ant-design-vue';

import {
  createTraceabilityBatch,
  deleteTraceabilityBatch,
  getTraceabilityBatch,
  getTraceabilityBatches,
  getTraceabilityTemplates,
  publishTraceabilityBatch,
  uploadTraceabilityImage,
  updateTraceabilityBatchStep,
} from '#/api';
import type { TraceabilityApi } from '#/api';

import { formatFieldValue, getFieldTypeLabel, normalizeFieldInput } from './shared';

const loading = ref(false);
const selectedBatchId = ref('');
const templates = ref<TraceabilityApi.TemplateSummary[]>([]);
const batches = ref<TraceabilityApi.BatchSummary[]>([]);
const batchDetail = ref<null | TraceabilityApi.BatchDetail>(null);
const stepIndex = ref(0);
const editableStartIndex = ref(0);
const createBatchVisible = ref(false);
const formState = reactive({
  batchCode: '',
  batchName: '',
  coverImage: '',
  productName: '',
  summary: '',
  tagsText: '',
  templateId: '',
});
const saving = ref(false);
const uploadingFieldKey = ref('');
const publishedTemplates = computed(() =>
  templates.value.filter((item) => item.status === 'active'),
);

const currentStep = computed(
  () => batchDetail.value?.steps?.[stepIndex.value] ?? null,
);
const isPublished = computed(() => batchDetail.value?.status === 'published');
const isLockedStep = computed(() => !!currentStep.value?.locked);
const actualCurrentStepIndex = computed(() =>
  isPublished.value ? (batchDetail.value?.currentStep ?? 0) : editableStartIndex.value,
);
const isCurrentEditableStep = computed(() => !!batchDetail.value && !isPublished.value);
const isLastStep = computed(() => {
  if (!batchDetail.value?.steps?.length) return false;
  return actualCurrentStepIndex.value >= batchDetail.value.steps.length - 1;
});
const allStepsCompleted = computed(() =>
  (batchDetail.value?.steps ?? []).every((item) => item.status === 'completed'),
);
const stepActionText = computed(() =>
  isLastStep.value ? '保存并发布' : '保存并切换至下一节点',
);

async function loadLists() {
  loading.value = true;
  try {
    templates.value = await getTraceabilityTemplates();
    batches.value = await getTraceabilityBatches();
    if (!selectedBatchId.value && batches.value[0]) {
      await selectBatch(batches.value[0].id);
    }
  } finally {
    loading.value = false;
  }
}

function applyBatch(detail: TraceabilityApi.BatchDetail) {
  batchDetail.value = structuredClone(detail);
  formState.batchCode = detail.batchCode;
  formState.batchName = detail.batchName;
  formState.coverImage = detail.coverImage;
  formState.productName = detail.productName;
  formState.summary = detail.summary;
  formState.tagsText = detail.tags.join(',');
  formState.templateId = detail.templateId;
  stepIndex.value = detail.currentStep ?? 0;
  editableStartIndex.value = detail.currentStep ?? 0;
}

async function selectBatch(id: string) {
  selectedBatchId.value = id;
  const detail = await getTraceabilityBatch(id);
  applyBatch(detail);
}

function openCreateBatchModal() {
  if (publishedTemplates.value.length === 0) {
    message.warning('请先在管理员页发布模板后，再新建批次');
    return;
  }
  selectedBatchId.value = '';
  batchDetail.value = null;
  formState.batchCode = `TR-${new Date().getFullYear()}-${String(Date.now()).slice(-6)}`;
  formState.batchName = '新建批次';
  formState.coverImage = '';
  formState.productName = '';
  formState.summary = '';
  formState.tagsText = '';
  formState.templateId = publishedTemplates.value[0]?.id ?? '';
  stepIndex.value = 0;
  createBatchVisible.value = true;
}

async function createBatch() {
  if (!formState.templateId) {
    message.warning('请先选择模板');
    return;
  }
  saving.value = true;
  try {
    const detail = await createTraceabilityBatch({
      batchCode: formState.batchCode,
      batchName: formState.batchName,
      coverImage: formState.coverImage,
      productName: formState.productName,
      summary: formState.summary,
      tags: formState.tagsText
        .split(',')
        .map((item) => item.trim())
        .filter(Boolean),
      templateId: formState.templateId,
    });
    message.success('批次已创建');
    createBatchVisible.value = false;
    await loadLists();
    if (detail?.id) {
      await selectBatch(detail.id);
    } else if (batches.value[0]) {
      await selectBatch(batches.value[0].id);
    }
  } finally {
    saving.value = false;
  }
}

function getBatchStatusText(status: string) {
  if (status === 'published') return '已发布';
  if (status === 'draft') return '未发布';
  return status || '进行中';
}

function getStepStatusText(index: number) {
  if (allStepsCompleted.value) {
    return '已完成';
  }
  return index < actualCurrentStepIndex.value ? '已完成' : '进行中';
}

function updateFieldValue(field: TraceabilityApi.FieldDefinition, value: any) {
  if (!currentStep.value) return;
  currentStep.value.values[field.key] = normalizeFieldInput(field, value);
}

function sanitizeIntegerInput(value: string) {
  const cleaned = value.replaceAll(/[^\d-]/g, '');
  const hasLeadingMinus = cleaned.startsWith('-');
  const unsigned = hasLeadingMinus ? cleaned.slice(1).replaceAll('-', '') : cleaned.replaceAll('-', '');
  return hasLeadingMinus ? `-${unsigned}` : unsigned;
}

function sanitizeDecimalInput(value: string) {
  const cleaned = value.replaceAll(/[^\d.-]/g, '');
  const firstDot = cleaned.indexOf('.');
  const normalizedDot =
    firstDot === -1
      ? cleaned
      : `${cleaned.slice(0, firstDot + 1)}${cleaned
          .slice(firstDot + 1)
          .replaceAll('.', '')}`;
  const firstMinus = normalizedDot.indexOf('-');
  return firstMinus <= 0
    ? normalizedDot
    : `-${normalizedDot.replaceAll('-', '')}`;
}

function getFieldUploadKey(field: TraceabilityApi.FieldDefinition) {
  return `${currentStep.value?.id ?? 'step'}:${field.key}`;
}

function getFieldInputId(field: TraceabilityApi.FieldDefinition) {
  return `traceability-upload-${getFieldUploadKey(field)}`;
}

function triggerImageSelect(field: TraceabilityApi.FieldDefinition) {
  const input = document.getElementById(getFieldInputId(field));
  input?.click();
}

function clearImageValue(field: TraceabilityApi.FieldDefinition) {
  updateFieldValue(field, '');
}

async function handleImageUpload(field: TraceabilityApi.FieldDefinition, event: Event) {
  const files = (event.target as HTMLInputElement).files;
  const file = files?.[0];
  if (!file || !currentStep.value || !selectedBatchId.value) return;

  const uploadKey = getFieldUploadKey(field);
  uploadingFieldKey.value = uploadKey;
  try {
    const formData = new FormData();
    formData.append('file', file);
    formData.append(
      'objectDir',
      `traceability/${selectedBatchId.value}/${currentStep.value.id}/${field.key}`,
    );
    const result = await uploadTraceabilityImage(formData);
    updateFieldValue(field, result.tempUrl || result.objectName);
    message.success('图片上传成功');
  } catch {
    message.error('图片上传失败');
  } finally {
    uploadingFieldKey.value = '';
    (event.target as HTMLInputElement).value = '';
  }
}

function removeBatch(id: string) {
  Modal.confirm({
    title: '删除批次',
    content: '删除后该批次的填报记录和发布信息都会一起清除，是否继续？',
    async onOk() {
      await deleteTraceabilityBatch(id);
      message.success('批次已删除');
      if (selectedBatchId.value === id) {
        selectedBatchId.value = '';
        batchDetail.value = null;
      }
      await loadLists();
    },
  });
}

async function saveStep() {
  if (!selectedBatchId.value || !currentStep.value) return;
  if (!isCurrentEditableStep.value || isPublished.value) {
    message.warning('请先完成当前进行中的节点');
    return;
  }
  saving.value = true;
  try {
    const detail = await updateTraceabilityBatchStep(
      selectedBatchId.value,
      currentStep.value.id,
      {
        completedAt: new Date().toISOString(),
        operatorName: currentStep.value.operatorName,
        status: 'completed',
        values: currentStep.value.values,
      },
    );
    applyBatch(detail);
    if (detail.steps.every((item) => item.status === 'completed')) {
      const published = await publishTraceabilityBatch(selectedBatchId.value);
      applyBatch(published);
      message.success('最后一个节点已保存并发布');
    } else {
      const nextIndex = detail.currentStep ?? 0;
      editableStartIndex.value = nextIndex;
      stepIndex.value = nextIndex;
      message.success('当前节点已保存，已切换至下一节点');
    }
    await loadLists();
  } finally {
    saving.value = false;
  }
}

onMounted(async () => {
  await loadLists();
});
</script>

<template>
  <Page auto-content-height>
    <div class="trace-operator">
      <Row :gutter="[16, 16]">
        <Col :lg="7" :md="8" :sm="24" :xs="24">
          <Card :loading="loading" class="panel-card batch-panel-card" title="批次流程">
            <template #extra>
              <Button type="primary" @click="openCreateBatchModal">新建批次</Button>
            </template>
            <div class="batch-list">
              <button
                v-for="item in batches"
                :key="item.id"
                class="batch-card"
                :class="{ active: item.id === selectedBatchId }"
                @click="selectBatch(item.id)"
              >
                <div class="batch-card__header">
                  <strong>{{ item.batchName }}</strong>
                  <div class="batch-card__actions">
                    <Tag>{{ getBatchStatusText(item.status) }}</Tag>
                    <Button danger size="small" type="link" @click.stop="removeBatch(item.id)">
                      删除
                    </Button>
                  </div>
                </div>
                <p>{{ item.batchCode }}</p>
                <div class="batch-card__meta">
                  <span>{{ item.templateName }}</span>
                  <span>{{ item.productName || '未设置产品名称' }}</span>
                  <span>扫码 {{ item.scanCount }} 次</span>
                </div>
                <small>{{ item.summary || '暂无批次概述' }}</small>
              </button>
            </div>
          </Card>
        </Col>

        <Col :lg="17" :md="16" :sm="24" :xs="24">
          <Space direction="vertical" size="middle" style="width: 100%">
            <Card v-if="batchDetail?.publishedAt" class="panel-card" title="发布信息">
              <div class="publish-panel">
                <div>
                  <span>溯源码</span>
                  <strong>{{ batchDetail.batchCode }}</strong>
                </div>
                <div>
                  <span>消费者访问地址</span>
                  <strong>{{ batchDetail.publicUrl }}</strong>
                </div>
                <div>
                  <span>发布时间</span>
                  <strong>{{ batchDetail.publishedAt || '未发布' }}</strong>
                </div>
                <div>
                  <span>当前状态</span>
                  <strong>{{ getBatchStatusText(batchDetail.status) }}</strong>
                </div>
              </div>
            </Card>

            <Card class="panel-card" title="节点填报">
              <template #extra>
                <Button
                  v-if="!isPublished"
                  type="primary"
                  :disabled="!currentStep || !isCurrentEditableStep"
                  :loading="saving"
                  @click="saveStep"
                >
                  {{ stepActionText }}
                </Button>
              </template>

              <template v-if="batchDetail">
                <Steps
                  class="step-strip"
                  :class="{ 'step-strip--published': isPublished }"
                  :current="stepIndex"
                  size="small"
                  @change="
                    (value) => {
                      stepIndex = value;
                      if (!isPublished) {
                        editableStartIndex = value;
                      }
                    }
                  "
                >
                  <Steps.Step
                    v-for="(item, index) in batchDetail.steps"
                    :key="item.id"
                    :title="item.name"
                    :description="getStepStatusText(index)"
                  />
                </Steps>

                <div
                  v-if="currentStep"
                  class="step-editor"
                  :class="{ 'step-editor--published': isPublished }"
                >
                  <div class="step-header">
                    <div>
                      <h3>{{ currentStep.name }}</h3>
                      <p>{{ currentStep.description || '请填报此环节的过程记录。' }}</p>
                      <small class="step-hint">
                        {{
                          isPublished
                            ? '当前批次已发布，溯源链已锁定为只读。'
                            : isLockedStep
                            ? '当前节点来自节点库，字段和值固定，不可修改。'
                            : isCurrentEditableStep
                            ? '当前节点可填写并继续流转。'
                            : '当前查看的是非进行中节点，仅供浏览。'
                        }}
                      </small>
                    </div>
                    <Tag color="blue">
                      {{ currentStep.category === 'public' ? '公开节点' : '业务节点' }}
                    </Tag>
                  </div>

                  <Row :gutter="[16, 16]">
                    <Col :md="12" :xs="24">
                      <label class="field-label">操作人</label>
                      <Input v-model:value="currentStep.operatorName" :disabled="!isCurrentEditableStep" />
                    </Col>
                    <Col :md="12" :xs="24">
                      <label class="field-label">节点状态</label>
                      <div class="readonly-box">{{ getStepStatusText(stepIndex) }}</div>
                    </Col>
                  </Row>

                  <Row :gutter="[16, 16]" class="dynamic-fields">
                    <Col
                      v-for="field in currentStep.fields"
                      :key="field.key"
                      :md="12"
                      :xs="24"
                    >
                      <div class="field-entry">
                        <div class="field-entry__head">
                          <label class="field-label">{{ field.label }}</label>
                          <span class="field-type-tag">{{ getFieldTypeLabel(field.type) }}</span>
                        </div>
                        <div class="field-entry__body">
                            <Select
                              v-if="field.type === 'select'"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :options="(field.options || []).map((item) => ({ label: item, value: item }))"
                            :value="currentStep.values[field.key]"
                            style="width: 100%"
                            @update:value="(value) => updateFieldValue(field, value)"
                          />
                            <Select
                              v-else-if="field.type === 'multi_select'"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :options="(field.options || []).map((item) => ({ label: item, value: item }))"
                            :value="currentStep.values[field.key]"
                            mode="multiple"
                            style="width: 100%"
                            @update:value="(value) => updateFieldValue(field, value)"
                          />
                          <Input
                            v-else-if="field.type === 'integer'"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :placeholder="field.placeholder || '请输入整数'"
                            :value="String(currentStep.values[field.key] ?? '')"
                            style="width: 100%"
                            @update:value="
                              (value) =>
                                updateFieldValue(field, sanitizeIntegerInput(String(value ?? '')))
                            "
                          />
                          <Input
                            v-else-if="field.type === 'decimal'"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :placeholder="field.placeholder || '请输入小数'"
                            :value="String(currentStep.values[field.key] ?? '')"
                            style="width: 100%"
                            @update:value="
                              (value) =>
                                updateFieldValue(field, sanitizeDecimalInput(String(value ?? '')))
                            "
                          />
                            <DatePicker
                              v-else-if="field.type === 'date'"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :value="currentStep.values[field.key]"
                            style="width: 100%"
                            value-format="YYYY-MM-DD"
                            @update:value="(value) => updateFieldValue(field, value)"
                          />
                            <DatePicker
                              v-else-if="field.type === 'datetime'"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :value="currentStep.values[field.key]"
                            format="YYYY-MM-DD HH:mm:ss"
                            show-time
                            style="width: 100%"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            @update:value="(value) => updateFieldValue(field, value)"
                          />
                            <Input
                              v-else-if="field.type === 'link'"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :placeholder="field.placeholder || '请输入链接地址'"
                            :value="currentStep.values[field.key]"
                            @update:value="(value) => updateFieldValue(field, value)"
                          />
                            <Input
                              v-else-if="field.type === 'string'"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :placeholder="field.placeholder || '请输入内容'"
                            :value="currentStep.values[field.key]"
                            @update:value="(value) => updateFieldValue(field, value)"
                          />
                          <div
                            v-else-if="field.type === 'image'"
                            class="placeholder-uploader"
                          >
                            <strong>上传节点图片</strong>
                            <p>支持上传后直接回填图片地址，消费者端和预览页都可直接查看。</p>
                            <div
                              v-if="currentStep.values[field.key]"
                              class="image-preview-wrap"
                            >
                              <img
                                :src="String(currentStep.values[field.key])"
                                alt="节点图片"
                                class="image-preview"
                              />
                            </div>
                            <div class="upload-trigger">
                              <input
                                :id="getFieldInputId(field)"
                                accept="image/*"
                                class="upload-input"
                                type="file"
                                :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                                @change="(event) => handleImageUpload(field, event)"
                              />
                              <Button
                                :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                                :loading="uploadingFieldKey === getFieldUploadKey(field)"
                                size="small"
                                type="primary"
                                @click="triggerImageSelect(field)"
                              >
                                {{ currentStep.values[field.key] ? '重新上传' : '选择图片' }}
                              </Button>
                              <Button
                                v-if="currentStep.values[field.key]"
                                :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                                size="small"
                                @click="clearImageValue(field)"
                              >
                                移除图片
                              </Button>
                            </div>
                          </div>
                          <div
                            v-else-if="field.type === 'video_url'"
                            class="placeholder-uploader"
                          >
                            <strong>视频控件模板</strong>
                            <p>这里先预留视频控件位置，后续你可以补充视频上传或选择逻辑。</p>
                          </div>
                            <Input.TextArea
                            v-else
                            :auto-size="{ minRows: 3, maxRows: 5 }"
                            :disabled="!isCurrentEditableStep || isPublished || isLockedStep"
                            :placeholder="field.placeholder || '请输入内容'"
                            :value="currentStep.values[field.key]"
                            @update:value="(value) => updateFieldValue(field, value)"
                          />
                          <div class="field-preview">
                            当前值：{{ formatFieldValue(currentStep.values[field.key]) }}
                          </div>
                        </div>
                      </div>
                    </Col>
                  </Row>
                </div>
              </template>

              <Empty v-else description="先新建批次或从左侧选择批次" />
            </Card>
          </Space>
        </Col>
      </Row>
    </div>

    <Modal
      v-model:open="createBatchVisible"
      title="新建批次"
      ok-text="创建批次"
      cancel-text="取消"
      :confirm-loading="saving"
      @ok="createBatch"
    >
      <Row :gutter="[16, 16]">
        <Col :span="24">
          <label class="field-label">模板</label>
          <Select
            v-model:value="formState.templateId"
            :options="publishedTemplates.map((item) => ({ label: item.name, value: item.id }))"
            style="width: 100%"
          />
        </Col>
        <Col :md="12" :xs="24">
          <label class="field-label">批次名称</label>
          <Input v-model:value="formState.batchName" />
        </Col>
        <Col :md="12" :xs="24">
          <label class="field-label">批次编码</label>
          <Input v-model:value="formState.batchCode" />
        </Col>
        <Col :md="12" :xs="24">
          <label class="field-label">产品名称</label>
          <Input v-model:value="formState.productName" />
        </Col>
        <Col :md="12" :xs="24">
          <label class="field-label">封面图</label>
          <Input v-model:value="formState.coverImage" />
        </Col>
        <Col :md="12" :xs="24">
          <label class="field-label">标签</label>
          <Input v-model:value="formState.tagsText" placeholder="用逗号分隔" />
        </Col>
        <Col :span="24">
          <label class="field-label">批次概述</label>
          <Input.TextArea
            v-model:value="formState.summary"
            :auto-size="{ minRows: 3, maxRows: 5 }"
          />
        </Col>
      </Row>
    </Modal>

  </Page>
</template>

<style scoped>
.trace-operator {
  padding: 4px;
}

.panel-card {
  border-radius: 18px;
}

.batch-panel-card {
  height: 100%;
}

.batch-panel-card :deep(.ant-card-body) {
  height: calc(100% - 57px);
}

.batch-list {
  display: grid;
  gap: 12px;
}

.batch-card {
  width: 100%;
  border: 1px solid #edf1f7;
  background: #fff;
  border-radius: 16px;
  padding: 16px;
  text-align: left;
}

.batch-card.active {
  border-color: #adc4ff;
  background: #f5f8ff;
}

.batch-card__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

.batch-card__actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.batch-card p {
  margin: 10px 0 8px;
  font-weight: 600;
}

.batch-card__meta {
  display: grid;
  gap: 4px;
  margin-bottom: 8px;
}

.batch-card span,
.batch-card small {
  color: #8b96a8;
  font-size: 12px;
}

.field-label {
  display: block;
  margin-bottom: 8px;
  color: #556070;
  font-size: 13px;
  font-weight: 600;
}

.step-strip {
  margin-bottom: 20px;
  overflow: auto;
}

.step-editor {
  border-top: 1px solid #f0f2f5;
  padding-top: 20px;
}

.step-strip--published,
.step-editor--published {
  opacity: 0.58;
}

.step-header {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 20px;
}

.step-header h3 {
  margin: 0 0 8px;
}

.step-header p {
  margin: 0;
  color: #6b7280;
}

.step-hint {
  display: block;
  margin-top: 8px;
  color: #1d4ed8;
}

.dynamic-fields {
  margin-top: 8px;
}

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

.publish-panel div,
.field-entry__body,
.readonly-box,
.placeholder-uploader {
  border: 1px solid #edf1f7;
  border-radius: 16px;
  padding: 16px;
  background: #fafcff;
}

.publish-panel span,
.field-entry__body small,
.field-modal__meta {
  color: #7d8899;
  font-size: 12px;
}

.publish-panel strong {
  display: block;
  margin-top: 8px;
  font-size: 16px;
  word-break: break-all;
}

.readonly-box {
  min-height: 54px;
  display: flex;
  align-items: center;
  color: #556070;
}

.field-entry {
  display: grid;
  gap: 8px;
}

.field-entry__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

.field-entry__body {
  min-height: 120px;
  display: grid;
  gap: 10px;
}

.field-entry__body strong {
  display: block;
  margin: 8px 0;
  color: #1f2937;
  white-space: pre-wrap;
  word-break: break-word;
}

.field-type-tag {
  display: inline-flex;
  padding: 4px 10px;
  border-radius: 999px;
  background: #edf3ff;
  color: #1d4ed8;
  font-size: 12px;
}

.placeholder-uploader strong {
  display: block;
  margin-bottom: 8px;
}

.upload-trigger {
  display: inline-flex;
  margin-top: 4px;
}

.upload-input {
  display: none;
}

.image-preview-wrap {
  margin: 4px 0 2px;
}

.image-preview {
  display: block;
  max-width: 100%;
  max-height: 220px;
  border: 1px solid #dbe3f0;
  border-radius: 14px;
  object-fit: cover;
  background: #fff;
}

.field-preview {
  color: #7d8899;
  font-size: 12px;
}

.placeholder-uploader p {
  margin: 0;
  color: #6b7280;
  line-height: 1.7;
}
</style>
