<template>
  <div class="page-upload-check-data">
    <div
      class="text-sm text-gray-500 flex items-center flex-wrap space-x-2 mb-6 rounded bg-white p-4"
    >
      <span class="text-gray-600">上传检查数据</span>
      <div class="flex items-center">
        <div class="flex items-center justify-center">
          <i class="text-red-500">*</i>
        </div>
        <span class="text-gray-600">关联的档案号：</span>
        <SearchArchivesSelect
          v-model="archivesId"
          :disabled="!!route.query.archivesId || !!route.query.sn"
        ></SearchArchivesSelect>
      </div>
      <el-button type="primary" @click="fileUploadDialogVisible = true"
        >上传原始PDF或者图片（非必传）</el-button
      >
      <div class="flex items-center space-x-2">
        <span class="text-gray-600">检查数据</span>
        <el-select-v2
          v-model="institutionId"
          placeholder="请选择"
          filterable
          :options="agencyFillList"
          :props="{
            label: 'institutionName',
            value: 'id'
          }"
          @change="agencyChange"
        />
        <el-select
          v-model="templateId"
          placeholder="请选择"
          multiple
          collapse-tags
          filterable
          :disabled="!institutionId"
        >
          <el-option
            v-for="(item, index) in templateList"
            :key="index"
            :label="item.templateName"
            :value="item.id"
          ></el-option>
        </el-select>
        <el-button type="primary" @click="addTemplate">添加</el-button>
      </div>
      <div class="flex-1 flex items-center flex-wrap justify-end whitespace-nowrap">
        <el-button @click="router.back()">返回</el-button>
        <el-button type="primary" :disabled="!list.length" @click="save"
          >保存并上传</el-button
        >
        <el-button
          v-if="ocrDataErrList.length"
          type="danger"
          link
          @click="ocrDataErrVisible = true"
          >错误信息</el-button
        >
      </div>
    </div>
    <div class="flex" style="height: calc(100vh - 180px)">
      <div v-if="layouts.length" class="h-full overflow-y-auto flex-1">
        <div
          v-for="(item, index) in layouts"
          :key="index"
          class="mb-2 rounded-b relative bg-white"
        >
          <div class="p-2 bg-white rounded-t">
            <span class="text-gray-500 text-sm">第{{ index + 1 }}页</span>
            <el-button
              v-if="ocrStatus"
              type="primary"
              class="ml-4"
              link
              @click="item.layoutVisible = !item.layoutVisible"
              >{{item.layoutVisible ? '返回展示原始内容' : '页面文字识别'}}</el-button
            >
          </div>
          <div v-if="item.layoutVisible" class="space-y-2 p-4">
            <div
              v-for="(line, lineIdx) in item.layout"
              :key="lineIdx"
              class="space-x-2"
            >
              <span
                v-for="(l, idx) in line"
                :key="idx"
                v-copy="l.label"
                class="inline-block p-1 border border-solid border-gray-300 hover:bg-gray-100 text-gray-700 hover:text-gray-900 cursor-copy"
                :class="
                  [
                    'border-red-300',
                    'border-yellow-400',
                    'border-emerald-300',
                    'border-blue-300',
                    'border-purple-300',
                    'border-orange-300',
                    'border-lime-300',
                    'border-cyan-300',
                    'border-pink-300',
                    'border-rose-300'
                  ][idx]
                "
                >{{ l.label }}</span
              >
            </div>
          </div>
          <img
            :src="item.url"
            alt=""
            class="w-full h-fit"
            :class="{
              'hidden opacity-0 invisible': item.layoutVisible
            }"
          />
        </div>
      </div>
      <div class="h-full overflow-y-auto flex-1">
        <div
          class="table-wrap bg-white mb-4 rounded p-4"
          v-for="(item, index) in list"
          :key="item.id"
        >
          <div class="table-toolbar flex items-center justify-between">
            <div class="flex-1 px-4">
              <span class="text-gray-500">{{ item.templateName }}</span>
              <!-- <span class="text-xs text-white">{{ item.id }}</span> -->
            </div>
            <div class="space-x-4">
              <el-date-picker
                v-model="item.date"
                type="datetime"
                placeholder="请选择日期"
                format="YYYY-MM-DD HH:mm:ss"
                value-format="YYYY-MM-DD HH:mm:ss"

                :disabled-date="disabledDate"
              />
              <el-popconfirm
                title="是否确认删除?"
                @confirm="list.splice(index, 1)"
              >
                <template #reference>
                  <el-button type="danger">删除</el-button>
                </template>
              </el-popconfirm>
            </div>
          </div>
          <ProofTable
            v-model:info="list[index]"
            v-model:edit="idEditView"
            :idx="index"
            :list="list"
          />
        </div>
      </div>
    </div>
    <el-dialog
      title="原始资料管理"
      v-model="fileUploadDialogVisible"
      width="30%"
    >
      <p class="text-lg font-semibold mb-2">选择上传的原始资料文件类型</p>
      <p class="mb-2">
        仅支持选择其中的1种类型上传，重新上传将会覆盖之前的数据
      </p>
      <!-- 上传按钮组 -->
      <div class="mb-4">
        <el-upload
          v-if="!fileUploadType || fileUploadType == 1"
          ref="uploadPDFRef"
          v-model:file-list="files"
          action="#"
          class="mb-4"
          :limit="1"
          :on-exceed="handleExceed"
          accept=".pdf"
          :http-request="uploadPDF"
          :show-file-list="false"
        >
          <template #trigger>
            <el-button type="primary">上传PDF文件（仅支持上传1份）</el-button>
          </template>
        </el-upload>
        <el-upload
          v-if="!fileUploadType || fileUploadType == 2"
          ref="uploadImgRef"
          v-model:file-list="files"
          action="#"
          accept=".jpg,.png,.jpeg"
          multiple
          :http-request="uploadImages"
          :show-file-list="false"
        >
          <template #trigger>
            <el-button type="primary">上传图片文件（可上传多张）</el-button>
          </template>
        </el-upload>
      </div>

      <!-- 图片格式提示 -->
      <p class="mb-4">图片类型支持常见的图片格式，如png，jpg，jpeg</p>

      <!-- 删除按钮 -->
      <el-popconfirm
        title="是否确认删除?"
        @confirm="
          fileUploadType = 0;
          fileUrls = [];
          files = [];
          layouts = [];
        "
      >
        <template #reference>
          <el-button type="danger">删除当前上传的文件</el-button>
        </template>
      </el-popconfirm>
    </el-dialog>

    <el-dialog v-model="ocrDataErrVisible" title="错误信息">
      <div
        v-for="(item, index) in ocrDataErrList"
        :key="index"
        class="border border-solid border-gray-200 rounded shadow-sm mb-4 p-4 space-y-2"
      >
        <div v-if="!item.errType && item.originalName">
          <span class="font-bold text-base my-1 mr-2">检查名称:</span>
          <el-tag type="warning">{{ item.originalName }}</el-tag>
        </div>
        <div v-if="!item.errType">
          <span class="font-bold text-base my-1 mr-2">系统名称:</span>
          <el-tag v-copy="item.nodeName" class="cursor-copy">{{ item.nodeName }}</el-tag>
        </div>
        <div>
          <span class="font-bold text-base my-1 mr-2">错误信息:</span>
          <div>
            <div
              v-for="(msg, msgIdx) in item.errMessage"
              :key="msgIdx"
              class="break-all whitespace-pre-wrap"
            >
              {{ msg }}
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
import { ElMessage, ElMessageBox, genFileId } from "element-plus";
import { ref, inject, shallowRef, defineAsyncComponent, provide } from "vue";
import {
  randomString,
  uploadToOSS,
  debounce,
  sleep,
  parseJSON,
  request
} from "@/utils";
import dayjs from "dayjs";
import SearchArchivesSelect from "@/components/Archives/SearchArchivesSelect.vue";
import ProofTable from "./components/ProofTable.vue";

import { useRoute, useRouter } from "vue-router";
const [route, router] = [useRoute(), useRouter()];

const list = ref([]);
const archivesId = ref(route.query.archivesId || "");
const institutionId = ref("");
const templateId = ref([]);
const idEditView = ref(true);

const ocrStatus = ref();
const getOcrStatus = async () => {
  const { data } = await request.get("/ocrService/setting/ocr/status");
  ocrStatus.value = data.ocr;
};
getOcrStatus();

const disabledDate = d => {
  const today = new Date();
  today.setHours(23, 59, 59, 999);
  return d.getTime() > today.getTime();
};
const groupItemsByDifference = (data, difference) => {
  const result = [];
  let currentRow = [];

  // 按 t 值升序排序
  data.sort((a, b) => a.t - b.t);

  for (let i = 0; i < data.length; i++) {
    const currentItem = data[i];

    // 如果当前行为空，直接将当前项目加入到当前行
    if (currentRow.length === 0) {
      currentRow.push(currentItem);
    } else {
      // 计算当前项目与当前行最后一个项目的 t 值差值
      const diff = currentItem.t - currentRow[currentRow.length - 1].t;

      // 如果差值小于等于给定的差值，则将当前项目加入到当前行
      if (diff <= difference) {
        currentRow.push(currentItem);
      } else {
        // 如果差值大于给定的差值，则将当前行加入到结果数组，并重新开始新的一行
        result.push(currentRow);
        currentRow = [currentItem];
      }
    }
  }

  // 将最后一行加入到结果数组
  if (currentRow.length > 0) {
    result.push(currentRow);
  }
  // 在每一行中按照 l 值升序排序
  result.forEach(row => {
    row.sort((a, b) => a.l - b.l);
  });

  return result;
};
const layouts = ref([]);
console.log(layouts.value);
const ocrLayout = debounce(async () => {
  if (!ocrStatus.value) return;
  const { data } = await request.post("/ocrService/label/layout", {
    urls: fileUrls.value.map(v => v.url),
    batchId: ""
  });
  layouts.value = (data.layouts || []).map(page => {
    let layout = page.layout[0].map(v => {
      return {
        label: v[1][0],
        l: v[0][0][0],
        t: v[0][0][1]
      };
    });
    return {
      ...page,
      layoutVisible: false,
      layout: groupItemsByDifference(layout, 12)
    };
  });
}, 300);
const ocrPageLayout = () => {};
const files = ref([]);
const fileUrls = ref([]);
const fileUploadType = ref(0);
const fileUploadDialogVisible = ref(false);
const uploadPDFRef = ref();
const handleExceed = files => {
  uploadPDFRef.value.clearFiles();
  const file = files[0];
  file.uid = genFileId();
  uploadPDFRef.value.handleStart(file);
};
const uploadPDF = async opt => {
  fileUploadType.value = 1;
  console.log(opt.file);
  const url = await uploadToOSS(opt.file, opt.file.name);
  files.value[0].url = url;
  fileUrls.value = [
    {
      name: opt.file.name,
      url
    }
  ];
  fileUploadDialogVisible.value = false;
  ocrLayout();
};

const uploadImgRef = ref();
const uploadImages = async opt => {
  fileUploadType.value = 2;
  console.log(opt);

  const url = await uploadToOSS(opt.file, opt.file.name);
  files.value.find(v => v.name == opt.file.name).url = url;
  fileUrls.value.push({
    name: opt.file.name,
    url
  });
  fileUploadDialogVisible.value = false;
  ocrLayout();
};

const agencyFillList = ref([]);
const getAgencyFillList = async () => {
  const { data } = await request.get(
    `/dataService/dataTemplate/mechanism/institution/paginate`,
    {
      params: {
        page: 1,
        pageSize: 1000
      }
    }
  );
  agencyFillList.value = data.list;
};
getAgencyFillList();
const templateList = ref([]);
const agencyChange = async () => {
  const { data } = await request.get(
    `/dataService/dataTemplate/mechanism/paginate`,
    {
      params: {
        page: 1,
        pageSize: 1000,
        institutionId: institutionId.value
      }
    }
  );
  templateId.value = []
  templateList.value = data.list;
};

const formatChildProjectChildren = (rowExtra, args) => {
  let extra = {
    ...rowExtra,
    curChildIndex: [],
    ...args
  };
  const children = [];
  if (extra.abnormal && extra.abnormal.length) {
    extra.abnormal.forEach(v => {
      try {
        v.properties.props = v.properties.props
          ? typeof v.properties.props == "string"
            ? JSON.parse(v.properties.props)
            : v.properties.props
          : "";
      } catch (e) {
        console.log(e, v.properties.props);
      }
      if (!v.nodeId && extra?.node?.id) {
        v.nodeId = extra.node.id;
      }
      if (Array.isArray(v.properties.props)) {
        v.properties.props.forEach(attr => {
          delete attr.values;
          delete attr.types;
          attr.val = "";
        });
      }
    });
  }
  return extra;
};
provide("formatChildProjectChildren", formatChildProjectChildren);

const recognitionType = ["身体物质/部位", "医学检查项目"];
const relatedRelationship = ["异常属于", "可检测"];
const catchRelateNodes = {};
const searchRelateNodes = async (id, relationship, query = {}) => {
  if (catchRelateNodes[id + relatedRelationship[relationship]])
    return catchRelateNodes[id + relatedRelationship[relationship]];
  const { data } = await request.get(`/graphService/open/node/related`, {
    params: { id, relationship: relatedRelationship[relationship], ...query }
  });
  catchRelateNodes[id + relatedRelationship[relationship]] = data || [];
  return data || [];
};
provide("searchRelateNodes", searchRelateNodes);
const searchAddNodes = async (key = "", isAddSearch = true, opt) => {
  if (!key) return;
  const { data } = await request.get(`/graphService/open/node/paginate`, {
    params: {
      pageSize: 500,
      name: key,
      tag: recognitionType[!isAddSearch && opt ? opt.type : 0],
      ...(!isAddSearch && opt ? opt.params || {} : {})
    }
  });
  if (isAddSearch) {
    addIndicatorQuery.value = "";
    allIndicators.value = data.list;
  }
  return data.list || [];
};
provide("searchAddNodes", searchAddNodes);

const getDefaultItem = (opt = {}) => {
  return {
    annotation: {
      id: ""
    },
    id: randomString(),
    label: "indicator",
    isPreviewModel: false,
    node: [],
    nodes: [],
    matchProject: [],
    indicators: [],
    pageName: "page-0",
    pageNum: 0,
    header: ["key", "value", "unit", "scope"],
    rows: [],
    children: [],
    ...opt
  };
};
const formatItems = async arr => {
  let item = getDefaultItem();
  item.rows = arr.map(v => {
    return [
      {
        infer: v.name,
        extra: null
      },
      {
        infer: v.value,
        extra: null
      },
      {
        infer: v.unit,
        extra: null
      },
      {
        infer: v.scope,
        extra: null
      }
    ];
  });
  return item;
};
const formatTemplateItem = async v => {
  const appendChildren = [];
  let item = getDefaultItem({
    templateId: v.id,
    templateName: v.templateName,
    date: ""
  });
  for (const k2 in v.templateItems) {
    const v2 = v.templateItems[k2];
    item.nodes[k2] = [v2.body];
    item.node[k2] = v2.body?.id;
    item.indicators[k2] = [v2.item];
    item.matchProject[k2] = v2.item?.id;
    const abnormal =
      (await searchRelateNodes(v2.body.id, 0, { filter: 1 })) || [];
    let valueLimit;
    if (v2.body && v2.body.properties?.props) {
      v2.body.properties.props = parseJSON(v2.body.properties?.props, "");
      if (
        Array.isArray(v2.body.properties.props) &&
        v2.body.properties.props[0]
      ) {
        valueLimit = v2.body.properties.props[0];
      }
    }
    item.rows.push([
      {
        infer: v2.name,
        extra: {
          abnormal: [abnormal],
          node: v2.body,
          projects: [v2.item],
          valueLimit
        }
      },
      {
        infer: v2.defaultValue,
        extra: null
      },
      {
        infer: v2.unit,
        extra: null
      },
      {
        infer: v2.scope,
        extra: null
      }
    ]);

    abnormal.length &&
      appendChildren.push({
        id: v2.body.id,
        abnormal,
        rowIdx: k2
      });
  }
  const temps = [];
  appendChildren.forEach(child => {
    const temp = formatChildProjectChildren(child, { rowIdx: child.rowIdx });
    temps.push(temp);
  });
  item.children = temps;
  return item;
};

let addTemplateLock = false;
const addTemplate = async () => {
  if (addTemplateLock) return;
  addTemplateLock = true;
  try {
    for (const v of templateId.value) {
      const template = templateList.value.find(v2 => v2.id == v);
      template.templateItems = parseJSON(template.templateItems);
      const item = await formatTemplateItem(template);
      console.log(item);
      list.value.push(item);
      templateId.value = [];
    }
  } catch (e) {
    ElMessage.error(e.message);
  }
  addTemplateLock = false;
};
const formatData = async () => {
  console.log(list.value);
  const result = [];

  list.value.forEach(item => {
    const bodyData = [];
    const abnormalData = [];
    if (item.children?.length) {
      item.children.forEach(child => {
        child.curChildIndex.forEach(k => {
          const childProject = child.abnormal[k];
          const abnormalItem = {
            id: childProject.id,
            name: childProject.properties?.name,
            value: null
          };
          if (childProject.properties.props) {
            const prop = {};
            childProject.properties.props.forEach(attr => {
              prop[attr.name] = String(attr.val);
            });
            abnormalItem.value = prop;
          }
          abnormalData.push(abnormalItem);
        });
      });
    }
    item.rows.map((row, rowIdx) => {
      const extra = row[0].extra;
      let bodyItem = {
        name: "",
        id: "",
        value: "",
        unit: "",
        reference: "",
        referStart: "",
        referEnd: "",
        itemId: item.matchProject[rowIdx],
        itemName: item.matchProject[rowIdx]
          ? item.indicators[rowIdx].find(v => v.id == item.matchProject[rowIdx])
              ?.properties?.name
          : "",
        abnormalId: [],
        abnormalDatas: []
      };
      bodyItem["originalName"] = row[0]?.infer || "";
      item.header.forEach((key, idx) => {
        if (item.label == "indicator") {
          key = key == "key" ? "name" : key;
          key = key == "scope" ? "reference" : key;
          if (key === "reference" && row[idx]?.extra) {
            bodyItem["reference"] = row[idx]?.extra.origin;
            bodyItem["referStart"] = row[idx]?.extra.start;
            bodyItem["referEnd"] = row[idx]?.extra.end;
          } else {
            bodyItem[key] = row[idx]?.infer || "";
          }
        }
      });
      if (item.label == "text") {
        bodyItem["value"] = row[0]?.infer || "";
      }
      const findChildren = item.children.find(v => v.rowIdx == rowIdx);
      if (findChildren) {
        console.log("findChildren", findChildren, bodyItem);
        bodyItem.abnormalId = findChildren.curChildIndex.map(i => {
          const abnormalItem = {
            id: findChildren.abnormal[i].id,
            name: findChildren.abnormal[i]?.properties?.name,
            nodeId: findChildren.abnormal[i].nodeId,
            value: null
          };
          if (findChildren.abnormal[i].properties.props) {
            const prop = {};
            findChildren.abnormal[i].properties.props.forEach(attr => {
              prop[attr.name] = String(attr.val);
            });
            abnormalItem.value = prop;
          }
          bodyItem.abnormalDatas.push(abnormalItem);
          return {
            id: abnormalItem.id,
            nodeId: abnormalItem.nodeId,
            name: abnormalItem.name
          };
        });
      }
      if (extra) {
        console.log(extra);
        bodyItem.id = extra.node.id;
        bodyItem.name = extra.node.properties?.name;

        bodyData.push(bodyItem);
      } else {
        if (item.label == "indicator" && item.node[rowIdx]) {
          const curNode = item.nodes[rowIdx].find(
            v => v.id == item.node[rowIdx]
          );
          bodyData.push({
            ...bodyItem,
            id: item.node[rowIdx],
            name: curNode?.properties?.name
          });
        }
        if (item.label == "text") {
          if (item.node[rowIdx]?.length) {
            for (const nodeId of item.node[rowIdx]) {
              const curNode = item.nodes[rowIdx].find(v => v.id == nodeId);
              console.log("curNode", curNode, bodyItem);
              bodyData.push({
                ...bodyItem,
                id: nodeId,
                name: curNode?.properties?.name
              });
            }
          }
        }
      }
    });
    bodyData.forEach(v => {
      v.abnormalId = v.abnormalId.filter(v2 => v2.nodeId == v.id);
      v.abnormalDatas = v.abnormalDatas.filter(v2 => v2.nodeId == v.id);
    });
    console.log(bodyData);
    console.log(abnormalData);
    result.push({
      bodyData,
      abnormalData,
      surveyData: "",
      groupId: item.templateId,
      groupName: item.templateName,
      date: dayjs(item.date).unix()
    });
  });
  return new Promise((resolve, reject) => {
    resolve(result);
  });
};

const ocrDataErrVisible = ref(false);
const ocrDataErrList = ref([]);

const checkData = async ocrData => {
  const { data } = await request.post(`/idcService/mechanism/medicalData/algor/preCheck`, {
    data: ocrData
  });
  ocrDataErrList.value = data.result?.errList || [];
  ocrDataErrVisible.value = !!ocrDataErrList.value.length;
  return data.result?.isCheckPass;
};
let saveLock = false;
const save = async () => {
  if (!archivesId.value) {
    return ElMessage.error("请选择关联档案");
  }
  for (const item of list.value) {
    if (!item.date) {
      return ElMessage.error("请选择日期");
    }
  }
  if (saveLock) return;
  saveLock = true;
  try {
    await ElMessageBox.confirm(
      "确定要提交数据？请核对数据发生日期与报告日期是否一致",
      "提示"
    );
    const req = await formatData();
    console.log("sendData===>", req);

    const isCheckPass = await checkData(req);
    if (!isCheckPass) {
      ElMessage.error("存在错误信息");
      saveLock = false;
      return false;
    }
    // return false
    if (route.query.sn) {
      await request.post(
        `/idcService/mechanism/medicalData/update/transferResult`,
        {
          sn: route.query.sn,
          attachData: JSON.stringify(list.value),
          data: req
        }
      );
    } else {
      await request.post(`/medicalData/create`, {
        type: 4,
        archivesId: archivesId.value,
        date: dayjs(Date.now()).format("YYYY-MM-DD HH:mm:ss"),
        attachData: JSON.stringify(list.value),
        transferRawJson: JSON.stringify(req),
        files: files.value.map(v => {
          return {
            fileName: v.name,
            fileUrl: v.url,
            remark: "",
            date: dayjs(req[0].date * 1000).format("YYYY-MM-DD")
          };
        })
      });
    }

    ElMessage.success("保存成功");
    await sleep(3e2);
    router.back();
  } catch (e) {
    console.log(e);
  }
  saveLock = false;
};

const getDetail = async () => {
  const { data } = await request.get(`/medicalData/detail`, {
    params: {
      sn: route.query.sn
    }
  });

  archivesId.value = data.detail.archivesId;
  list.value = parseJSON(data.detail.attachData);
  fileUrls.value = (data.detail.files || []).map(v => {
    return {
      ...v,
      name: v.fileName,
      url: v.fileUrl
    };
  });
  fileUrls.value.length && ocrLayout();
};

const init = async () => {
  if (route.query.sn) {
    getDetail();
  }
};
init();
</script>
<style lang="scss">
.el-scrollbar__view {
  height: 100%;
  .main-content.page-upload-check-data {
    margin: 0;
  }
}
.page-upload-check-data {
  .data-container {
    height: calc(100% - 48px);
    width: 100%;
  }
}
</style>
