<template>
  <section class="risk-detail-section">
    <div class="section-card">
      <div class="section-header">
        <div>
          <div class="section-title">风险明细</div>
          <div class="section-subtitle">展示涉疑交易与异常账户关联人员信息</div>
        </div>
        <el-button size="mini" type="text" :disabled="!projectId" @click="handleRiskDetailExport">
          导出
        </el-button>
      </div>

      <div class="block">
        <div class="block-header">
          <div>
            <div class="block-title">涉疑交易明细</div>
            <div class="block-subtitle">展示涉疑交易的关键字段与风险金额</div>
          </div>
        </div>
        <el-form :inline="true" size="mini" class="suspicious-filter-form" @submit.native.prevent>
          <el-form-item label="命中模型">
            <el-select
              v-model="selectedModelCode"
              class="suspicious-filter-select"
              placeholder="全部模型"
              filterable
              :disabled="isExternalSuspiciousType"
            >
              <el-option label="全部模型" value="" />
              <el-option
                v-for="item in riskModelOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="可疑规则">
            <el-select
              v-model="currentSuspiciousType"
              class="suspicious-filter-select suspicious-type-select"
              placeholder="全部可疑规则"
              @change="handleSuspiciousTypeSelectChange"
            >
              <el-option
                v-for="item in suspiciousTypeOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" icon="el-icon-search" :loading="suspiciousLoading" @click="handleSuspiciousQuery">
              查询
            </el-button>
          </el-form-item>
        </el-form>

        <el-table
          v-loading="suspiciousLoading"
          :data="suspiciousTransactionList"
          class="result-table"
        >
          <template slot="empty">
            <el-empty :image-size="96" description="当前筛选条件下暂无涉疑交易明细" />
          </template>
          <el-table-column prop="trxDate" label="交易时间" min-width="180" />
          <el-table-column label="本方账户" min-width="220">
            <template slot-scope="scope">
              <div class="multi-line-cell">
                <div class="primary-text">{{ formatField(scope.row.leAccountNo) }}</div>
                <div class="secondary-text">{{ formatField(scope.row.leAccountName) }}</div>
              </div>
            </template>
          </el-table-column>
          <el-table-column label="对方账户" min-width="220">
            <template slot-scope="scope">
              <div class="multi-line-cell">
                <div class="primary-text">
                  {{ formatCounterpartyName(scope.row) }}
                </div>
                <div class="secondary-text">
                  {{ formatField(scope.row.customerAccountNo) }}
                </div>
              </div>
            </template>
          </el-table-column>
          <el-table-column label="关联员工" min-width="160">
            <template slot-scope="scope">
              <div class="multi-line-cell">
                <div class="primary-text">{{ formatRelatedStaff(scope.row) }}</div>
              </div>
            </template>
          </el-table-column>
          <el-table-column label="摘要 / 交易类型" min-width="240">
            <template slot-scope="scope">
              <div class="multi-line-cell">
                <div class="primary-text">{{ formatField(scope.row.userMemo) }}</div>
                <div class="secondary-text">{{ formatField(scope.row.cashType) }}</div>
              </div>
            </template>
          </el-table-column>
          <el-table-column label="异常标签" min-width="220">
            <template slot-scope="scope">
              <div v-if="scope.row.hitTags && scope.row.hitTags.length" class="hit-tag-list">
                <el-tag
                  v-for="(tag, index) in scope.row.hitTags"
                  :key="`${scope.row.bankStatementId}-tag-${index}`"
                  size="mini"
                  :type="mapRiskLevelToTagType(tag.riskLevel)"
                  effect="plain"
                >
                  {{ tag.ruleName }}
                </el-tag>
              </div>
              <span v-else class="empty-text">-</span>
            </template>
          </el-table-column>
          <el-table-column prop="displayAmount" label="交易金额" min-width="140" align="right">
            <template slot-scope="scope">
              <span
                class="amount-text"
                :class="scope.row.displayAmount >= 0 ? 'amount-in' : 'amount-out'"
              >
                {{ formatAmount(scope.row.displayAmount) }}
              </span>
            </template>
          </el-table-column>
          <el-table-column label="详情" width="100" fixed="right" align="center">
            <template slot-scope="scope">
              <el-button type="text" size="mini" @click="handleViewDetail(scope.row)">
                详情
              </el-button>
            </template>
          </el-table-column>
        </el-table>

        <pagination
          v-show="suspiciousTotal > 0"
          :total="suspiciousTotal"
          :page.sync="suspiciousPageNum"
          :limit.sync="suspiciousPageSize"
          :page-sizes="riskDetailPageSizeOptions"
          layout="total, sizes, prev, pager, next, jumper"
          @pagination="handlePageChange"
        />
      </div>

      <div class="block">
        <div class="block-header">
          <div>
            <div class="block-title">员工负面征信信息</div>
            <div class="block-subtitle">展示当前项目员工的负面征信信息</div>
          </div>
        </div>

        <el-table
          v-loading="employeeCreditNegativeLoading"
          :data="employeeCreditNegativeList"
          class="result-table"
        >
          <template slot="empty">
            <el-empty :image-size="96" description="当前项目暂无员工负面征信信息" />
          </template>
          <el-table-column prop="personName" label="员工姓名" min-width="140" />
          <el-table-column label="身份证号" min-width="180" show-overflow-tooltip>
            <template slot-scope="scope">
              {{ formatIdCard(scope.row.personId) }}
            </template>
          </el-table-column>
          <el-table-column prop="queryDate" label="最近征信查询日期" min-width="160" />
          <el-table-column prop="civilCnt" label="民事案件笔数" min-width="120" align="center" />
          <el-table-column label="民事案件金额" min-width="140" align="right">
            <template slot-scope="scope">
              {{ formatAmount(scope.row.civilLmt) }}
            </template>
          </el-table-column>
          <el-table-column prop="enforceCnt" label="强制执行笔数" min-width="120" align="center" />
          <el-table-column label="强制执行金额" min-width="140" align="right">
            <template slot-scope="scope">
              {{ formatAmount(scope.row.enforceLmt) }}
            </template>
          </el-table-column>
          <el-table-column prop="admCnt" label="行政处罚笔数" min-width="120" align="center" />
          <el-table-column label="行政处罚金额" min-width="140" align="right">
            <template slot-scope="scope">
              {{ formatAmount(scope.row.admLmt) }}
            </template>
          </el-table-column>
        </el-table>

        <pagination
          v-show="employeeCreditNegativeTotal > 0"
          :total="employeeCreditNegativeTotal"
          :page.sync="employeeCreditNegativePageNum"
          :limit.sync="employeeCreditNegativePageSize"
          :page-sizes="riskDetailPageSizeOptions"
          layout="total, sizes, prev, pager, next, jumper"
          @pagination="handleEmployeeCreditNegativePageChange"
        />
      </div>

      <div class="block">
        <div class="block-header">
          <div>
            <div class="block-title">异常账户人员信息</div>
            <div class="block-subtitle">展示异常账户关联人员与处理状态</div>
          </div>
        </div>

        <el-table
          v-loading="abnormalAccountLoading"
          :data="abnormalAccountList"
          class="result-table"
        >
          <template slot="empty">
            <el-empty :image-size="96" description="当前项目暂无异常账户人员信息" />
          </template>
          <el-table-column prop="accountNo" label="账号" min-width="160" />
          <el-table-column prop="accountName" label="开户人" min-width="120" />
          <el-table-column prop="bankName" label="银行" min-width="180" />
          <el-table-column prop="abnormalType" label="异常类型" min-width="160" />
          <el-table-column prop="abnormalTime" label="异常发生时间" min-width="160" />
          <el-table-column prop="status" label="状态" min-width="120" />
        </el-table>

        <pagination
          v-show="abnormalAccountTotal > 0"
          :total="abnormalAccountTotal"
          :page.sync="abnormalAccountPageNum"
          :limit.sync="abnormalAccountPageSize"
          :page-sizes="riskDetailPageSizeOptions"
          layout="total, sizes, prev, pager, next, jumper"
          @pagination="handleAbnormalAccountPageChange"
        />
      </div>
    </div>

    <el-dialog
      :visible.sync="detailVisible"
      append-to-body
      custom-class="detail-dialog"
      width="980px"
      @close="closeDetailDialog"
    >
      <template slot="title">
        <div class="detail-dialog-title">
          <span>流水详情</span>
          <el-button
            class="evidence-corner-btn"
            size="mini"
            plain
            :disabled="detailLoading || !buildFlowEvidenceFingerprint(detailData)"
            @click="handleAddEvidence"
          >
            加入证据库
          </el-button>
        </div>
      </template>
      <div v-loading="detailLoading" class="detail-dialog-body">
        <div class="detail-overview-grid">
          <div class="detail-field">
            <div class="detail-label">交易时间</div>
            <div class="detail-value">{{ formatField(detailData.trxDate) }}</div>
          </div>
          <div class="detail-field">
            <div class="detail-label">交易金额</div>
            <div class="detail-value amount-text" :class="getAmountClass(detailData.displayAmount)">
              {{ formatSignedAmount(detailData.displayAmount) }}
            </div>
          </div>
          <div class="detail-field">
            <div class="detail-label">交易后余额</div>
            <div class="detail-value">{{ formatAmount(detailData.amountBalance) }}</div>
          </div>

          <div class="detail-field">
            <div class="detail-label">本方主体</div>
            <div class="detail-value">{{ formatField(detailData.leAccountName) }}</div>
          </div>
          <div class="detail-field">
            <div class="detail-label">本方账号</div>
            <div class="detail-value">{{ formatField(detailData.leAccountNo) }}</div>
          </div>
          <div class="detail-field">
            <div class="detail-label">本方银行</div>
            <div class="detail-value">{{ formatField(detailData.bank) }}</div>
          </div>

          <div class="detail-field">
            <div class="detail-label">对方名称</div>
            <div class="detail-value">{{ formatCounterpartyName(detailData) }}</div>
          </div>
          <div class="detail-field">
            <div class="detail-label">对方账户</div>
            <div class="detail-value">{{ formatField(detailData.customerAccountNo) }}</div>
          </div>
          <div class="detail-field">
            <div class="detail-label">对方银行</div>
            <div class="detail-value">{{ formatField(detailData.customerBank) }}</div>
          </div>

          <div class="detail-field">
            <div class="detail-label">摘要</div>
            <div class="detail-value">{{ formatField(detailData.userMemo) }}</div>
          </div>
          <div class="detail-field">
            <div class="detail-label">交易类型</div>
            <div class="detail-value">{{ formatField(detailData.cashType) }}</div>
          </div>
          <div class="detail-field">
            <div class="detail-label">银行摘要</div>
            <div class="detail-value">{{ formatField(detailData.bankComments) }}</div>
          </div>

          <div class="detail-field detail-field--full">
            <div class="detail-label">原始文件</div>
            <div class="detail-file-block">
              <i class="el-icon-document detail-file-icon"></i>
              <div class="detail-file-meta">
                <div class="detail-file-name">{{ formatOriginalFileName(detailData) }}</div>
                <div class="detail-file-time">
                  上传时间：{{ formatOriginalFileUploadTime(detailData) }}
                </div>
              </div>
            </div>
          </div>
        </div>

        <div class="detail-hit-tag-section">
          <div class="detail-section-title">命中异常标签</div>
          <div
            v-if="detailData.hitTags && detailData.hitTags.length"
            class="detail-hit-tag-items"
          >
            <div
              v-for="(tag, index) in detailData.hitTags"
              :key="`detail-tag-${index}`"
              class="detail-hit-tag-item"
            >
              <div class="detail-hit-tag-header">
                <span class="detail-hit-tag-name">{{ formatField(tag.ruleName) }}</span>
                <el-tag size="mini" :type="mapRiskLevelToTagType(tag.riskLevel)" effect="plain">
                  {{ formatRiskLevel(tag.riskLevel) }}
                </el-tag>
              </div>
              <div class="detail-hit-tag-reason">{{ formatField(tag.reasonDetail) }}</div>
            </div>
          </div>
          <div v-else class="detail-hit-tag-empty">当前流水未命中异常标签</div>
        </div>
      </div>
      <div slot="footer" class="detail-dialog-footer">
        <el-button @click="closeDetailDialog">取消</el-button>
        <el-button type="primary" @click="closeDetailDialog">确定</el-button>
      </div>
    </el-dialog>
  </section>
</template>

<script>
import {
  getOverviewAbnormalAccountPeople,
  getOverviewEmployeeCreditNegative,
  getOverviewSuspiciousTransactions,
} from "@/api/ccdi/projectOverview";
import { getBankStatementDetail } from "@/api/ccdiProjectBankStatement";
import { buildFlowEvidenceFingerprint, buildFlowEvidenceSnapshot } from "@/utils/ccdiEvidence";

const SUSPICIOUS_TYPE_OPTIONS = [
  { value: "ALL", label: "全部可疑规则" },
  { value: "MODEL_RULE", label: "员工规则命中" },
  { value: "EXTERNAL_PERSON", label: "外部人员预警" },
];
const DEFAULT_RISK_DETAIL_PAGE_SIZE = 5;
const RISK_DETAIL_PAGE_SIZE_OPTIONS = [5, 10, 20, 50];

const normalizeSuspiciousType = (value) => (
  SUSPICIOUS_TYPE_OPTIONS.some((item) => item.value === value) ? value : "ALL"
);

const normalizeRiskModelOptions = (rows) => {
  if (!Array.isArray(rows)) {
    return [];
  }
  const seen = new Set();
  return rows
    .map((item) => ({
      value: item && (item.value || item.modelCode || item.key),
      label: item && (item.label || item.modelName || item.title),
    }))
    .filter((item) => {
      if (!item.value || seen.has(item.value)) {
        return false;
      }
      seen.add(item.value);
      return true;
    });
};

const ABNORMAL_CUSTOMER_TRANSACTION_RULE_CODE = "ABNORMAL_CUSTOMER_TRANSACTION";
const ABNORMAL_CUSTOMER_TRANSACTION_RULE_NAME = "与客户之间非正常资金往来";

const resolveAbnormalCustomerTransactionRuleName = (nameListHitType) => {
  if (nameListHitType === "中介") {
    return "疑似与中介往来";
  }
  if (nameListHitType === "信贷客户") {
    return "与信贷客户之间非正常资金往来";
  }
  return ABNORMAL_CUSTOMER_TRANSACTION_RULE_NAME;
};

const normalizeRuleName = (tag, context) => {
  if (!tag) {
    return "";
  }
  if (
    tag.ruleCode === ABNORMAL_CUSTOMER_TRANSACTION_RULE_CODE
    || tag.ruleName === "异常交易"
    || tag.ruleName === ABNORMAL_CUSTOMER_TRANSACTION_RULE_NAME
  ) {
    return resolveAbnormalCustomerTransactionRuleName(context && context.nameListHitType);
  }
  return tag.ruleName || "";
};

const normalizeHitTags = (hitTags, context = {}) => {
  if (!Array.isArray(hitTags)) {
    return [];
  }
  const seen = new Set();
  return hitTags
    .map((tag) => ({
      ...tag,
      ruleName: normalizeRuleName(tag, context),
    }))
    .filter((tag) => {
      const key = `${tag.ruleCode || ""}|${tag.ruleName || ""}`;
      if (seen.has(key)) {
        return false;
      }
      seen.add(key);
      return true;
    })
    .sort((left, right) => {
      const leftPriority = left.ruleCode === ABNORMAL_CUSTOMER_TRANSACTION_RULE_CODE ? 0 : 1;
      const rightPriority = right.ruleCode === ABNORMAL_CUSTOMER_TRANSACTION_RULE_CODE ? 0 : 1;
      return leftPriority - rightPriority;
    });
};

const createEmptyDetailData = () => ({
  bankStatementId: "",
  projectId: "",
  currency: "",
  trxDate: "",
  leAccountNo: "",
  leAccountName: "",
  customerAccountName: "",
  customerAccountNo: "",
  customerBank: "",
  customerReference: "",
  userMemo: "",
  bankComments: "",
  bankTrxNumber: "",
  bank: "",
  cashType: "",
  amountDr: "",
  amountCr: "",
  amountBalance: "",
  displayAmount: "",
  trxFlag: "",
  trxType: "",
  exceptionType: "",
  internalFlag: "",
  paymentMethod: "",
  cretNo: "",
  createDate: "",
  originalFileName: "",
  uploadTime: "",
  sourceFileName: "",
  fileName: "",
  hitTags: [],
});

const normalizeDetailData = (detail) => ({
  ...createEmptyDetailData(),
  ...(detail || {}),
  hitTags: normalizeHitTags(detail && detail.hitTags),
});

export default {
  name: "RiskDetailSection",
  props: {
    sectionData: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      suspiciousLoading: false,
      detailLoading: false,
      detailVisible: false,
      detailData: createEmptyDetailData(),
      currentSuspiciousType: "ALL",
      selectedModelCode: "",
      suspiciousPageNum: 1,
      suspiciousPageSize: DEFAULT_RISK_DETAIL_PAGE_SIZE,
      suspiciousTotal: 0,
      suspiciousTransactionList: [],
      riskModelOptions: [],
      employeeCreditNegativeLoading: false,
      employeeCreditNegativePageNum: 1,
      employeeCreditNegativePageSize: DEFAULT_RISK_DETAIL_PAGE_SIZE,
      employeeCreditNegativeTotal: 0,
      employeeCreditNegativeList: [],
      abnormalAccountLoading: false,
      abnormalAccountPageNum: 1,
      abnormalAccountPageSize: DEFAULT_RISK_DETAIL_PAGE_SIZE,
      abnormalAccountTotal: 0,
      abnormalAccountList: [],
      projectId: null,
      statementDetailCache: {},
    };
  },
  computed: {
    riskDetailPageSizeOptions() {
      return RISK_DETAIL_PAGE_SIZE_OPTIONS;
    },
    suspiciousTypeOptions() {
      return SUSPICIOUS_TYPE_OPTIONS;
    },
    isExternalSuspiciousType() {
      return this.currentSuspiciousType === "EXTERNAL_PERSON";
    },
  },
  watch: {
    sectionData: {
      immediate: true,
      deep: true,
      handler(value) {
        this.projectId = value && value.projectId ? value.projectId : null;
        this.currentSuspiciousType = normalizeSuspiciousType(value && value.suspiciousType);
        this.selectedModelCode = "";
        this.riskModelOptions = normalizeRiskModelOptions(value && value.riskModelOptions);
        this.suspiciousPageNum = 1;
        this.suspiciousPageSize = DEFAULT_RISK_DETAIL_PAGE_SIZE;
        this.suspiciousTotal = Number(value && value.total) || 0;
        this.employeeCreditNegativePageNum = 1;
        this.employeeCreditNegativePageSize = DEFAULT_RISK_DETAIL_PAGE_SIZE;
        this.employeeCreditNegativeTotal = Number(value && value.employeeCreditNegativeTotal) || 0;
        this.abnormalAccountPageNum = 1;
        this.abnormalAccountPageSize = DEFAULT_RISK_DETAIL_PAGE_SIZE;
        this.abnormalAccountTotal = 0;
        this.abnormalAccountList = [];
        const rows = Array.isArray(value && value.suspiciousTransactionList)
          ? value.suspiciousTransactionList
          : [];
        this.employeeCreditNegativeList = Array.isArray(value && value.employeeCreditNegativeList)
          ? value.employeeCreditNegativeList
          : [];
        this.hydrateSuspiciousRows(rows);
        this.loadAbnormalAccountPeople();
      },
    },
  },
  methods: {
    buildFlowEvidenceFingerprint,
    handleSuspiciousTypeSelectChange(value) {
      if (value === "EXTERNAL_PERSON") {
        this.selectedModelCode = "";
      }
    },
    async handleSuspiciousQuery() {
      this.suspiciousPageNum = 1;
      await this.loadSuspiciousTransactions();
    },
    async handlePageChange(pageInfo) {
      if (typeof pageInfo === "number") {
        this.suspiciousPageNum = pageInfo;
      } else {
        this.suspiciousPageNum = pageInfo.page;
        this.suspiciousPageSize = pageInfo.limit || this.suspiciousPageSize;
      }
      await this.loadSuspiciousTransactions();
    },
    async handleEmployeeCreditNegativePageChange(pageInfo) {
      if (typeof pageInfo === "number") {
        this.employeeCreditNegativePageNum = pageInfo;
      } else {
        this.employeeCreditNegativePageNum = pageInfo.page;
        this.employeeCreditNegativePageSize = pageInfo.limit || this.employeeCreditNegativePageSize;
      }
      await this.loadEmployeeCreditNegative();
    },
    async handleAbnormalAccountPageChange(pageInfo) {
      if (typeof pageInfo === "number") {
        this.abnormalAccountPageNum = pageInfo;
      } else {
        this.abnormalAccountPageNum = pageInfo.page;
        this.abnormalAccountPageSize = pageInfo.limit || this.abnormalAccountPageSize;
      }
      await this.loadAbnormalAccountPeople();
    },
    async loadSuspiciousTransactions() {
      if (!this.projectId) {
        this.suspiciousTransactionList = [];
        this.suspiciousTotal = 0;
        this.suspiciousLoading = false;
        return;
      }

      this.suspiciousLoading = true;
      try {
        const response = await getOverviewSuspiciousTransactions({
          projectId: this.projectId,
          modelCode: this.selectedModelCode,
          suspiciousType: this.currentSuspiciousType,
          pageNum: this.suspiciousPageNum,
          pageSize: this.suspiciousPageSize,
        });
        const data = (response && response.data) || {};
        this.suspiciousTotal = Number(data.total) || 0;
        await this.hydrateSuspiciousRows(Array.isArray(data.rows) ? data.rows : []);
      } catch (error) {
        this.suspiciousTransactionList = [];
        this.suspiciousTotal = 0;
        this.$message.error("加载涉疑交易明细失败");
        console.error("加载涉疑交易明细失败", error);
        this.suspiciousLoading = false;
      }
    },
    async loadEmployeeCreditNegative() {
      if (!this.projectId) {
        this.employeeCreditNegativeList = [];
        this.employeeCreditNegativeTotal = 0;
        this.employeeCreditNegativeLoading = false;
        return;
      }

      this.employeeCreditNegativeLoading = true;
      try {
        const response = await getOverviewEmployeeCreditNegative({
          projectId: this.projectId,
          pageNum: this.employeeCreditNegativePageNum,
          pageSize: this.employeeCreditNegativePageSize,
        });
        const data = (response && response.data) || {};
        this.employeeCreditNegativeList = Array.isArray(data.rows) ? data.rows : [];
        this.employeeCreditNegativeTotal = Number(data.total) || 0;
      } catch (error) {
        this.employeeCreditNegativeList = [];
        this.employeeCreditNegativeTotal = 0;
        this.$message.error("加载员工负面征信信息失败");
        console.error("加载员工负面征信信息失败", error);
      } finally {
        this.employeeCreditNegativeLoading = false;
      }
    },
    async loadAbnormalAccountPeople() {
      if (!this.projectId) {
        this.abnormalAccountList = [];
        this.abnormalAccountTotal = 0;
        this.abnormalAccountLoading = false;
        return;
      }

      this.abnormalAccountLoading = true;
      try {
        const response = await getOverviewAbnormalAccountPeople({
          projectId: this.projectId,
          pageNum: this.abnormalAccountPageNum,
          pageSize: this.abnormalAccountPageSize,
        });
        const data = (response && response.data) || {};
        this.abnormalAccountList = Array.isArray(data.rows) ? data.rows : [];
        this.abnormalAccountTotal = Number(data.total) || 0;
      } catch (error) {
        this.abnormalAccountList = [];
        this.abnormalAccountTotal = 0;
        this.$message.error("加载异常账户人员信息失败");
        console.error("加载异常账户人员信息失败", error);
      } finally {
        this.abnormalAccountLoading = false;
      }
    },
    async hydrateSuspiciousRows(rows) {
      const safeRows = Array.isArray(rows) ? rows : [];
      if (!safeRows.length) {
        this.suspiciousTransactionList = [];
        this.suspiciousLoading = false;
        return;
      }

      this.suspiciousLoading = true;
      try {
        const enrichedRows = await Promise.all(
          safeRows.map(async (row) => {
            if (!row || !row.bankStatementId) {
              return {
                ...row,
                hitTags: [],
              };
            }
            try {
              const detail = await this.fetchStatementDetail(row.bankStatementId, true);
              return {
                ...detail,
                ...row,
                hitTags: normalizeHitTags(detail.hitTags, row),
              };
            } catch (error) {
              return {
                ...row,
                hitTags: [],
              };
            }
          })
        );
        this.suspiciousTransactionList = enrichedRows;
      } finally {
        this.suspiciousLoading = false;
      }
    },
    async fetchStatementDetail(bankStatementId, silent) {
      if (!bankStatementId) {
        return createEmptyDetailData();
      }

      if (this.statementDetailCache[bankStatementId]) {
        return this.statementDetailCache[bankStatementId];
      }

      try {
        const response = await getBankStatementDetail(bankStatementId);
        const detail = normalizeDetailData(response && response.data);
        this.$set(this.statementDetailCache, bankStatementId, detail);
        return detail;
      } catch (error) {
        if (!silent) {
          throw error;
        }
        return createEmptyDetailData();
      }
    },
    async handleViewDetail(row) {
      if (!row || !row.bankStatementId) {
        return;
      }

      this.detailVisible = true;
      this.detailLoading = true;
      try {
        const detail = await this.fetchStatementDetail(row.bankStatementId, false);
        this.detailData = {
          ...detail,
          ...row,
          hitTags: row && Array.isArray(row.hitTags) && row.hitTags.length
            ? normalizeHitTags(row.hitTags, row)
            : normalizeHitTags(detail.hitTags, row),
        };
      } catch (error) {
        this.detailData = createEmptyDetailData();
        this.$message.error("加载流水详情失败");
        console.error("加载流水详情失败", error);
      } finally {
        this.detailLoading = false;
      }
    },
    closeDetailDialog() {
      this.detailVisible = false;
      this.detailLoading = false;
      this.detailData = createEmptyDetailData();
    },
    handleAddEvidence() {
      const detail = this.detailData || {};
      const sourceRecordId = buildFlowEvidenceFingerprint(detail);
      const amountText = this.formatSignedAmount(detail.displayAmount);
      const counterparty = this.formatCounterpartyName(detail);
      const relatedPersonName = this.resolveFlowRelatedPerson(detail);
      const hitTagText = Array.isArray(detail.hitTags) && detail.hitTags.length
        ? `，命中${detail.hitTags.map((tag) => tag.ruleName).filter(Boolean).join("、")}标签`
        : "";
      this.$emit("evidence-confirm", {
        evidenceType: "FLOW",
        relatedPersonName,
        relatedPersonId: detail.cretNo || "",
        evidenceTitle: `${relatedPersonName} / ${this.formatField(detail.leAccountNo)}`,
        evidenceSummary: `${this.formatField(detail.trxDate)}，${relatedPersonName}账户与${counterparty}发生交易，金额${amountText}${hitTagText}。`,
        sourceType: "BANK_STATEMENT",
        sourceRecordId,
        sourcePage: "流水详情",
        snapshotJson: JSON.stringify(buildFlowEvidenceSnapshot(detail)),
      });
    },
    resolveFlowRelatedPerson(detail) {
      const value = this.formatField(detail.leAccountName);
      return value === "-" ? "关联人员" : value;
    },
    handleRiskDetailExport() {
      if (!this.projectId) {
        return;
      }
      this.download(
        "ccdi/project/overview/risk-details/export",
        {
          projectId: this.projectId,
        },
        `风险明细_${this.projectId}_${new Date().getTime()}.xlsx`
      );
    },
    formatRelatedStaff(row) {
      if (!row || !row.relatedStaffName) {
        return "-";
      }
      return row.relatedStaffCode
        ? `${row.relatedStaffName}(${row.relatedStaffCode})`
        : row.relatedStaffName;
    },
    formatSummaryAndCashType(row) {
      const summary = row && row.userMemo ? row.userMemo : "";
      const cashType = row && row.cashType ? row.cashType : "";
      return `${summary}/${cashType}`;
    },
    formatCounterpartyName(detail) {
      if (!detail) {
        return "-";
      }
      return this.formatField(detail.customerAccountName);
    },
    formatField(value) {
      if (value === null || value === undefined || value === "") {
        return "-";
      }
      return String(value);
    },
    formatIdCard(value) {
      if (value === null || value === undefined || value === "") {
        return "-";
      }
      return String(value);
    },
    formatAmount(value) {
      if (value === null || value === undefined || value === "") {
        return "-";
      }
      const amount = Number(value);
      if (Number.isNaN(amount)) {
        return "-";
      }
      return amount.toLocaleString("zh-CN", {
        minimumFractionDigits: 2,
        maximumFractionDigits: 2,
      });
    },
    formatSignedAmount(value) {
      if (value === null || value === undefined || value === "") {
        return "-";
      }
      const amount = Number(value);
      if (Number.isNaN(amount)) {
        return "-";
      }
      const text = this.formatAmount(amount);
      return amount >= 0 ? `+${text}` : `-${this.formatAmount(Math.abs(amount))}`;
    },
    getAmountClass(value) {
      if (value === null || value === undefined || value === "") {
        return "";
      }
      return Number(value) >= 0 ? "amount-in" : "amount-out";
    },
    formatOriginalFileName(detail) {
      if (!detail) {
        return "暂无原始文件";
      }
      return (
        detail.originalFileName ||
        detail.sourceFileName ||
        detail.fileName ||
        "暂无原始文件"
      );
    },
    formatOriginalFileUploadTime(detail) {
      if (!detail || !detail.uploadTime) {
        return "-";
      }
      return String(detail.uploadTime);
    },
    formatRiskLevel(value) {
      const level = String(value || "").toUpperCase();
      if (level === "HIGH") {
        return "高风险";
      }
      if (level === "MEDIUM") {
        return "中风险";
      }
      if (level === "LOW") {
        return "低风险";
      }
      return "未标注";
    },
    mapRiskLevelToTagType(riskLevel) {
      const level = String(riskLevel || "").toUpperCase();
      if (level === "HIGH") {
        return "danger";
      }
      if (level === "MEDIUM") {
        return "warning";
      }
      return "info";
    },
  },
};
</script>

<style lang="scss" scoped>
.risk-detail-section {
  margin-bottom: 16px;
}

.section-card {
  padding: 20px;
  border-radius: 0;
  background: #fff;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
}

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 18px;
  padding-left: 12px;
  border-left: 4px solid #2563eb;
}

.section-title {
  font-size: 20px;
  line-height: 28px;
  font-weight: 700;
  color: #0f172a;
}

.section-subtitle {
  margin-top: 6px;
  font-size: 12px;
  color: #64748b;
}

.block + .block {
  margin-top: 24px;
}

.block-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 14px;
}

.block-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.el-dropdown-link {
  font-size: 12px;
  color: #2563eb;
  cursor: pointer;
}

.block-title {
  position: relative;
  padding-left: 10px;
  font-size: 15px;
  font-weight: 600;
  color: #334155;
}

.block-title::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 4px;
  height: 14px;
  border-radius: 999px;
  background: #94a3b8;
  transform: translateY(-50%);
}

.block-subtitle {
  margin-top: 4px;
  padding-left: 10px;
  font-size: 12px;
  color: #94a3b8;
}

.suspicious-filter-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-bottom: 14px;
  padding: 10px 12px;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
}

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

.suspicious-filter-form :deep(.el-form-item__label) {
  color: #475569;
  font-weight: 500;
}

.suspicious-filter-select {
  width: 220px;
}

.suspicious-type-select {
  width: 160px;
}

.result-table {
  width: 100%;
}

.multi-line-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.primary-text {
  color: #303133;
  line-height: 20px;
}

.secondary-text {
  color: #909399;
  font-size: 12px;
  line-height: 18px;
}

.hit-tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.empty-text {
  color: var(--ccdi-text-muted);
  font-size: 13px;
  line-height: 20px;
}

.amount-text {
  font-weight: 600;
}

.amount-in {
  color: #67c23a;
}

.amount-out {
  color: #f56c6c;
}

.detail-dialog-body {
  min-height: 280px;
}

.detail-overview-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px 32px;
  margin-bottom: 24px;
}

.detail-field {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

.detail-field--full {
  grid-column: 1 / -1;
}

.detail-label {
  font-size: 14px;
  line-height: 20px;
  color: var(--ccdi-text-muted);
}

.detail-value {
  color: var(--ccdi-text-primary);
  line-height: 22px;
  font-size: 16px;
  font-weight: 500;
  word-break: break-all;
}

.detail-file-block {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 2px 0;
}

.detail-file-icon {
  margin-top: 2px;
  font-size: 20px;
  color: #a56a2a;
}

.detail-file-meta {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.detail-file-name {
  color: var(--ccdi-text-primary);
  line-height: 22px;
  word-break: break-all;
}

.detail-file-time {
  color: var(--ccdi-text-muted);
  font-size: 12px;
  line-height: 18px;
}

.detail-hit-tag-section {
  border-top: 1px solid var(--ccdi-line);
  padding-top: 24px;
}

.detail-section-title {
  margin-bottom: 16px;
  font-size: 16px;
  font-weight: 600;
  color: var(--ccdi-text-primary);
}

.detail-hit-tag-items {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.detail-hit-tag-item {
  padding: 12px 16px;
  border: 1px solid var(--ccdi-border);
  border-radius: 12px;
  background: #fbfcfe;
}

.detail-hit-tag-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.detail-hit-tag-name {
  color: var(--ccdi-text-primary);
  font-size: 14px;
  font-weight: 600;
  line-height: 22px;
  word-break: break-all;
}

.detail-hit-tag-reason {
  margin-top: 8px;
  color: var(--ccdi-text-secondary);
  font-size: 13px;
  line-height: 20px;
  word-break: break-word;
}

.detail-hit-tag-empty {
  color: var(--ccdi-text-muted);
  font-size: 13px;
  line-height: 20px;
}

.detail-dialog-footer {
  display: flex;
  justify-content: center;
  gap: 12px;
}

.detail-dialog-title {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.evidence-corner-btn {
  padding: 4px 9px;
  font-size: 12px;
  color: #5b7fb8;
  border-color: #d6e4f7;
  background: #f8fbff;

  &:hover {
    color: #2474e8;
    border-color: #9fc3ff;
    background: #edf5ff;
  }
}

:deep(.detail-dialog) {
  border-radius: 8px;

  .el-dialog__header {
    padding: 20px 24px;
    border-bottom: 1px solid var(--ccdi-line);
  }

  .el-dialog__title {
    font-size: 20px;
    font-weight: 600;
    color: var(--ccdi-text-primary);
  }

  .el-dialog__body {
    padding: 24px;
  }

  .el-dialog__footer {
    padding: 8px 24px 24px;
  }

  .el-button {
    min-width: 180px;
  }
}

@media (max-width: 992px) {
  .detail-overview-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 20px 24px;
  }
}

@media (max-width: 768px) {
  .suspicious-filter-form {
    align-items: stretch;
  }

  .suspicious-filter-form :deep(.el-form-item) {
    width: 100%;
  }

  .suspicious-filter-form :deep(.el-form-item__content),
  .suspicious-filter-select,
  .suspicious-type-select,
  .suspicious-filter-form :deep(.el-button) {
    width: 100%;
  }

  .detail-overview-grid {
    grid-template-columns: 1fr;
    gap: 18px;
  }

  .detail-dialog-footer {
    flex-direction: column;
  }

  .detail-hit-tag-header {
    flex-direction: column;
    align-items: stretch;
  }

  :deep(.detail-dialog) {
    width: calc(100vw - 24px) !important;
    margin-top: 8vh !important;

    .el-dialog__header,
    .el-dialog__body,
    .el-dialog__footer {
      padding-left: 16px;
      padding-right: 16px;
    }

    .el-button {
      width: 100%;
      min-width: 0;
    }
  }
}
</style>
