/**
 * 右栏 · 评查点详情卡片
 * 展示单个评查点的完整详情，复用 renderConsistencyRule/renderOtherRule/renderModelRule 渲染逻辑
 */
import { useState, useEffect, useRef, useMemo } from 'react';
import { createPortal } from 'react-dom';
import { toastService } from '~/components/ui/Toast';
import type { ReviewPoint, CharPosition, PdfBboxHighlight } from '../ReviewPointsList';
import { CorporateInfoModal } from '../../corporate-information';
import type { BusinessInfoResult, DishonestyResult } from '../../corporate-information';
import { queryCompanyInfo } from '~/api/corporate-information/qichacha';

interface ReviewPointDetailCardProps {
  reviewPoint: ReviewPoint;
  onReviewPointSelect: (id: string | number, page?: number, charPositions?: CharPosition[], value?: string, bboxHighlight?: PdfBboxHighlight) => void;
  onStatusChange: (id: string, editAuditStatusId: string | number, status: string, message: string) => void;
  fileFormat?: string;
  detailMode?: 'legacy' | 'leaudit';
}

// ── 比较方法映射 ──
const compareMethodMap: Record<string, string> = {
  exact: '精确匹配',
  contains: '包含关系',
  semantic: '大模型语义匹配',
};
const getCompareMethodText = (method?: string): string => {
  if (!method) return '相等';
  const text = compareMethodMap[method] || method;
  return typeof text === 'string' ? text : String(text);
};
const ruleTypeMap: Record<string, string> = {
  exists: '有无判断', format: '格式判断', logic: '逻辑判断', regex: '正则表达式',
};
const getRuleTypeText = (type?: string): string => {
  if (!type) return '';
  return ruleTypeMap[type] || type;
};

function normalizeActionContent(actionContent?: string | string[]): string {
  if (typeof actionContent === 'string') return actionContent;
  if (Array.isArray(actionContent)) {
    return actionContent
      .map(item => typeof item === 'string' ? item : JSON.stringify(item))
      .filter(Boolean)
      .join('\n');
  }
  return '';
}

function getLeauditNote(reviewPoint: ReviewPoint): string {
  return reviewPoint.editAuditStatusMessage || normalizeActionContent(reviewPoint.actionContent) || reviewPoint.suggestion || '';
}

function getLeauditRawFieldValue(value: ReviewPoint['content'][string]): unknown {
  if (value == null) return undefined;
  if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value;
  if (typeof value === 'object' && 'value' in value) return value.value;
  return value;
}

function isValidQuad(value: unknown): value is [number, number, number, number] {
  return Array.isArray(value) && value.length === 4 && value.every(item => typeof item === 'number' && Number.isFinite(item));
}

function getLeauditTargetPage(reviewPoint: ReviewPoint, fieldKey: string): number | undefined {
  const contentPage = reviewPoint.contentPage?.[fieldKey];
  const parsedContentPage = Number(contentPage);
  if (Number.isFinite(parsedContentPage) && parsedContentPage > 0) return parsedContentPage;

  const pageNum = reviewPoint.fieldPositions?.[fieldKey]?.page_num;
  if (typeof pageNum === 'number' && Number.isFinite(pageNum) && pageNum >= 0) return pageNum + 1;

  return undefined;
}

function getLeauditBboxHighlight(reviewPoint: ReviewPoint, fieldKey: string, page?: number): PdfBboxHighlight | undefined {
  const fieldPosition = reviewPoint.fieldPositions?.[fieldKey];
  if (!fieldPosition || !isValidQuad(fieldPosition.bbox) || !isValidQuad(fieldPosition.page_box)) return undefined;

  return {
    fieldKey,
    bbox: [...fieldPosition.bbox] as [number, number, number, number],
    pageBox: [...fieldPosition.page_box] as [number, number, number, number],
    pageNum: fieldPosition.page_num,
    page,
    confidence: fieldPosition.confidence,
    matchMethod: fieldPosition.match_method,
  };
}

// ── Tooltip 系统 ──
let activeTooltip = { show: false, content: null as React.ReactNode, position: { top: 0, left: 0 }, ready: false };
function TooltipPortal() {
  const [tooltip, setTooltip] = useState(activeTooltip);
  useEffect(() => {
    const update = () => setTooltip({ ...activeTooltip });
    window.addEventListener('tooltip-update', update);
    return () => window.removeEventListener('tooltip-update', update);
  }, []);
  if (!tooltip.show || !tooltip.content) return null;
  return createPortal(
    <div
      className="fixed bg-white shadow-lg rounded-md p-1 border border-gray-200 z-[9999]"
      style={{
        top: `${tooltip.position.top}px`, left: `${tooltip.position.left}px`,
        transform: 'translate(-100%, -50%)', opacity: tooltip.ready ? 1 : 0,
        visibility: tooltip.ready ? 'visible' : 'hidden', transition: 'opacity 0.15s ease-out',
      }}
    >
      {tooltip.content}
      <div className="absolute top-1/2 right-0 transform translate-x-1/2 -translate-y-1/2 rotate-45 w-2 h-2 bg-white border-t border-r border-gray-200" />
    </div>,
    document.body,
  );
}
function showTooltip(content: React.ReactNode, position: { top: number; left: number }) {
  activeTooltip = { show: true, content, position, ready: false };
  window.dispatchEvent(new Event('tooltip-update'));
  requestAnimationFrame(() => {
    const el = document.querySelector('.fixed.bg-white.shadow-lg.rounded-md') as HTMLElement;
    if (el) {
      const r = el.getBoundingClientRect();
      let t = position.top, l = position.left;
      if (l - r.width < 0) l = r.width + 10;
      if (t - r.height / 2 < 0) t = r.height / 2 + 10;
      if (t + r.height / 2 > window.innerHeight) t = window.innerHeight - r.height / 2 - 10;
      activeTooltip.position = { top: t, left: l };
      activeTooltip.ready = true;
    } else {
      activeTooltip.ready = true;
    }
    window.dispatchEvent(new Event('tooltip-update'));
  });
}
function hideTooltip() {
  activeTooltip.show = false;
  activeTooltip.ready = false;
  window.dispatchEvent(new Event('tooltip-update'));
}

// ── ReactTableTooltip ──
function renderReactTable(text: string) {
  const rows = text.split('\n').map(r => r.split('\t'));
  return (
    <table className="border-collapse text-[11px] w-full">
      <tbody>
        {rows.map((row, i) => (
          <tr key={i} className={i === 0 ? 'font-semibold bg-slate-100' : ''}>
            {row.map((cell, j) => <td key={j} className="border border-slate-200 px-2 py-1">{cell}</td>)}
          </tr>
        ))}
      </tbody>
    </table>
  );
}
function renderMarkdownTable(text: string) {
  const lines = text.split('\n').filter(l => l.trim());
  const rows: string[][] = [];
  for (const line of lines) {
    if (/^\s*\|[-\s:]+\|/.test(line)) continue;
    const cells = line.split('|').map(c => c.trim()).filter((_, i, a) => i > 0 && i < a.length);
    if (cells.length > 0) rows.push(cells);
  }
  if (rows.length < 1) return <span>{text}</span>;
  return (
    <table className="border-collapse text-[11px] w-full">
      <tbody>
        {rows.map((row, i) => (
          <tr key={i} className={i === 0 ? 'font-semibold bg-slate-100' : ''}>
            {row.map((cell, j) => <td key={j} className="border border-slate-200 px-2 py-1">{cell}</td>)}
          </tr>
        ))}
      </tbody>
    </table>
  );
}
function renderPipeTable(text: string) {
  const lines = text.split('\n').filter(l => l.includes('|'));
  const rows = lines.map(l => l.split('|').map(c => c.trim()).filter(Boolean));
  return (
    <table className="border-collapse text-[11px] w-full">
      <tbody>
        {rows.map((row, i) => (
          <tr key={i} className={i === 0 ? 'font-semibold bg-slate-100' : ''}>
            {row.map((cell, j) => <td key={j} className="border border-slate-200 px-2 py-1">{cell}</td>)}
          </tr>
        ))}
      </tbody>
    </table>
  );
}
const ReactTableTooltip = ({ content }: { content: string }) => {
  const [showTip, setShowTip] = useState(false);
  const [rendered, setRendered] = useState<React.ReactNode>(null);
  const ref = useRef<HTMLDivElement>(null);
  const isTabTable = content.includes('\t') && content.includes('\n');
  const isMdTable = content.includes('|') && /\|[-\s:]+\|/.test(content);
  const isPipeTable = !isMdTable && content.includes('|') && content.includes('\n') && content.split('\n').filter(l => l.includes('|')).length >= 2;
  const isTableLike = isTabTable || isMdTable || isPipeTable;
  useEffect(() => {
    const check = () => { if (ref.current) setShowTip(isTableLike || ref.current.scrollHeight > ref.current.clientHeight); };
    if (isMdTable) setRendered(renderMarkdownTable(content));
    else if (isPipeTable) setRendered(renderPipeTable(content));
    else if (isTabTable) setRendered(renderReactTable(content));
    else setRendered(content);
    requestAnimationFrame(check);
    window.addEventListener('resize', check);
    return () => window.removeEventListener('resize', check);
  }, [content, isTableLike]);
  return (
    <div className="relative">
      <div ref={ref} className="text-xs text-gray-700 line-clamp-2 select-text" onMouseEnter={() => showTip && showTooltip(<div className="p-2 max-w-md max-h-64 overflow-auto">{rendered}</div>, { top: ref.current?.getBoundingClientRect().top || 0, left: ref.current?.getBoundingClientRect().left || 0 })} onMouseLeave={hideTooltip}>{rendered}</div>
    </div>
  );
};

// ── filterOtherRule ──
interface MergedRule {
  fieldKey: string;
  fieldValue: {
    type: Record<string, { res: boolean; page?: number | string; value?: string; char_positions?: CharPosition[] }>;
  };
}

function filterOtherRule(reviewPoint: ReviewPoint): MergedRule[] {
  interface RuleFieldValue { page?: number | string; value?: string; char_positions?: CharPosition[]; type: Record<string, boolean> }
  const allRule: Array<{ fieldKey: string; fieldValue: RuleFieldValue }> = [];

  for (const rule of reviewPoint.evaluatedPointResultsLog?.rules || []) {
    if ((rule.config as any).res !== reviewPoint.result) continue;

    if (rule.type === 'exists') {
      const config = rule.config as { res: boolean; fields: Record<string, { page: number; value: string; char_positions?: CharPosition[] }>; logic?: string };
      if (config.res) {
        Object.entries(config.fields).forEach(([key, fv]) => {
          if (fv.value && fv.value.trim() !== '') allRule.push({ fieldKey: key, fieldValue: { ...fv, type: { exists: true } } });
        });
      } else {
        Object.entries(config.fields).forEach(([key, fv]) => {
          const empty = !fv.value || fv.value.trim() === '';
          allRule.push({ fieldKey: key, fieldValue: { ...fv, type: { exists: !empty } } });
        });
      }
    }
    if (rule.type === 'format') {
      const config = rule.config as { res: boolean; field: Record<string, { page: string | number; value: string; char_positions?: CharPosition[] }>; formatType?: string; parameters?: string };
      if (config.field) {
        const entries = Object.entries(config.field);
        if (entries.length > 0) { const [key, fv] = entries[0]; allRule.push({ fieldKey: key, fieldValue: { ...fv, type: { format: config.res } } }); }
      }
    }
    if (rule.type === 'logic') {
      const config = rule.config as { logic: string; res: boolean; conditions: Array<{ field: Record<string, { page: number | string; value: string; char_positions?: CharPosition[] }>; value: string; operator: string; res: boolean }> };
      if (config.conditions && Array.isArray(config.conditions)) {
        config.conditions.forEach(cond => {
          const entries = Object.entries(cond.field);
          if (entries.length > 0) { const [key, fv] = entries[0]; allRule.push({ fieldKey: key, fieldValue: { ...fv, type: { logic: cond.res } } }); }
        });
      }
    }
    if (rule.type === 'regex') {
      const config = rule.config as { res: boolean; field: Record<string, { page: number | string; value: string; char_positions?: CharPosition[] }>; pattern?: string };
      if (config.field) {
        const entries = Object.entries(config.field);
        if (entries.length > 0) { const [key, fv] = entries[0]; allRule.push({ fieldKey: key, fieldValue: { ...fv, type: { regex: config.res } } }); }
      }
    }
  }

  const fieldKeyMap: Record<string, MergedRule> = {};
  allRule.forEach(item => {
    const typeKey = Object.keys(item.fieldValue.type)[0];
    const typeValue = item.fieldValue.type[typeKey];
    if (!fieldKeyMap[item.fieldKey]) fieldKeyMap[item.fieldKey] = { fieldKey: item.fieldKey, fieldValue: { type: {} } };
    fieldKeyMap[item.fieldKey].fieldValue.type[typeKey] = { res: typeValue, page: item.fieldValue.page, value: item.fieldValue.value, char_positions: item.fieldValue.char_positions };
  });
  return Object.values(fieldKeyMap);
}

// ── renderOtherRule ──
function RenderOtherRule({ rule, reviewPoint, onReviewPointSelect }: { rule: MergedRule; reviewPoint: ReviewPoint; onReviewPointSelect: (id: string | number, page?: number, charPos?: CharPosition[], value?: string, bboxHighlight?: PdfBboxHighlight) => void }) {
  const fieldKey = rule.fieldKey;
  const fieldValue = rule.fieldValue;
  const hasFailure = Object.values(fieldValue.type || {}).some(item => item.res === false);
  const overallResult = !hasFailure;
  const failedEntry = Object.entries(fieldValue.type || {}).find(([, item]) => item.res === false);
  const mainEntry = failedEntry || Object.entries(fieldValue.type || {})[0];
  if (!mainEntry) return null;
  const [, mainVal] = mainEntry;

  const tooltipContent = (
    <div className="flex flex-row gap-2 overflow-x-auto max-h-[300px]">
      {Object.entries(fieldValue.type || {}).map(([tk, tv]) => (
        <div key={tk} className="rounded-md flex flex-row items-center">
          <div className="text-xs text-gray-600 pl-1 whitespace-nowrap">{getRuleTypeText(tk)}:</div>
          <div className="p-1 text-xs rounded-full min-w-[50px] text-center">{tv.res ? '通过' : '不通过'}</div>
        </div>
      ))}
    </div>
  );

  return (
    <button
      className={`border border-gray rounded-md overflow-hidden mb-2 ${overallResult ? 'bg-[rgba(246,255,237,1)]' : 'bg-[rgba(255,251,230,1)]'} flex w-full text-left hover:shadow-[0_0_10px_rgba(0,0,0,0.1)] ${overallResult ? 'hover:bg-[rgba(0,128,0,0.1)]' : 'hover:bg-[rgba(255,255,0,0.1)]'}`}
      onClick={(e) => {
        e.stopPropagation();
        if (mainVal.page && typeof onReviewPointSelect === 'function') onReviewPointSelect(reviewPoint.id, Number(mainVal.page), mainVal.char_positions, mainVal.value);
        else if (reviewPoint.contentPage && reviewPoint.contentPage[fieldKey]) onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[fieldKey]), mainVal.char_positions, mainVal.value);
        else toastService.error(`没有找到${fieldKey}对应的索引内容`);
      }}
      type="button"
    >
      <div className="p-1 flex-1">
        <div className="text-xs text-gray-500 mb-1">
          {fieldKey}
          {!mainVal.page && !(reviewPoint.contentPage && reviewPoint.contentPage[fieldKey]) && <i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容" />}
          {mainVal.res === false && !mainVal.value && <span className="ml-2 text-xs text-yellow-500">缺失</span>}
        </div>
        {mainVal.value && <ReactTableTooltip content={mainVal.value} />}
      </div>
      <div className="w-8 flex items-center justify-center rounded-r-md" onMouseEnter={(e) => { const r = e.currentTarget.getBoundingClientRect(); showTooltip(tooltipContent, { top: r.top + r.height / 2, left: r.left }); }} onMouseLeave={hideTooltip}>
        {overallResult ? <i className="ri-check-line text-success text-base" /> : <i className="ri-alert-line text-warning text-base" />}
      </div>
    </button>
  );
}

// ── renderConsistencyRule ──
type ChainItem = { field: string; data: { key: string; page: number; value: string; char_positions?: CharPosition[] }; res: boolean; compareMethod?: string };

function RenderConsistencyRule({ rule, reviewPoint, onReviewPointSelect }: { rule: Record<string, unknown>; reviewPoint: ReviewPoint; onReviewPointSelect: (id: string | number, page?: number, charPos?: CharPosition[], value?: string, bboxHighlight?: PdfBboxHighlight) => void }) {
  if (reviewPoint.result !== (rule.res as boolean)) return null;
  const config = rule.config as { logic?: string; pairs?: Array<{ sourceField: Record<string, { page: number; value: string; char_positions?: CharPosition[] }>; targetField: Record<string, { page: number; value: string; char_positions?: CharPosition[] }>; res: boolean; compareMethod?: string }>; selectedFields?: string[] } | undefined;
  if (!config || !config.pairs || !Array.isArray(config.pairs) || config.pairs.length === 0) return null;
  const pairs = config.pairs;

  const chains = useMemo(() => {
    type CI = ChainItem;
    const result: Array<Array<CI>> = [];
    const visited = new Set<string>();
    const fieldMap = new Map<string, Array<{ targetField: string; data: { source: { key: string; page: number; value: string; char_positions?: CharPosition[] }; target: { key: string; page: number; value: string; char_positions?: CharPosition[] } }; res: boolean; compareMethod?: string }>>();
    pairs.forEach(pair => {
      const sKey = Object.keys(pair.sourceField)[0], tKey = Object.keys(pair.targetField)[0];
      if (!fieldMap.has(sKey)) fieldMap.set(sKey, []);
      fieldMap.get(sKey)?.push({ targetField: tKey, data: { source: { key: sKey, ...pair.sourceField[sKey] }, target: { key: tKey, ...pair.targetField[tKey] } }, res: pair.res, compareMethod: pair.compareMethod });
    });
    const starts = new Set<string>();
    for (const [key] of fieldMap.entries()) { let isT = false; for (const p of pairs) { if (Object.keys(p.targetField)[0] === key) { isT = true; break; } } if (!isT) starts.add(key); }
    for (const sp of starts) {
      if (visited.has(sp)) continue;
      const chain: CI[] = [];
      let cur = sp;
      while (fieldMap.has(cur)) {
        const targets = fieldMap.get(cur);
        if (!targets || targets.length === 0) break;
        let next: typeof targets[0] | null = null;
        for (const t of targets) { if (!visited.has(t.targetField)) { next = t; break; } }
        if (!next) break;
        if (chain.length === 0) chain.push({ field: cur, data: next.data.source, res: next.res, compareMethod: next.compareMethod });
        chain.push({ field: next.targetField, data: next.data.target, res: next.res, compareMethod: next.compareMethod });
        visited.add(cur); visited.add(next.targetField);
        cur = next.targetField;
      }
      if (chain.length > 0) result.push(chain);
    }
    // isolated pairs
    for (const pair of pairs) {
      const sKey = Object.keys(pair.sourceField)[0], tKey = Object.keys(pair.targetField)[0];
      if (!visited.has(sKey) || !visited.has(tKey)) {
        result.push([
          { field: sKey, data: { key: sKey, ...pair.sourceField[sKey] }, res: pair.res },
          { field: tKey, data: { key: tKey, ...pair.targetField[tKey] }, res: pair.res },
        ]);
        visited.add(sKey); visited.add(tKey);
      }
    }
    return result;
  }, [pairs]);

  return (
    <div className="mt-3">
      <div className="comparison-group">
        {chains.map((chain: ChainItem[], ci: number) => {
          const res = chain[1]?.res ?? true;
          const itemCls = res ? 'comparison-item match' : 'comparison-item mismatch';

          if (chain.length > 2) {
            return (
              <div key={`chain_${ci}`} className={`${itemCls} border border-gray rounded-md overflow-hidden mb-2 ${res ? 'bg-[rgba(246,255,237,1)]' : 'bg-[rgba(255,251,230,1)]'} hover:shadow-[0_0_10px_rgba(0,0,0,0.1)] cursor-pointer`}
                onClick={(e) => { e.stopPropagation(); for (const item of chain) { if (item.data.page && typeof onReviewPointSelect === 'function') { onReviewPointSelect(reviewPoint.id, Number(item.data.page), item.data.char_positions); break; } } }}>
                <div className="comparison-values flex w-full">
                  <div className="value-box p-2 pb-1 flex-1">
                    <div className="value-source text-xs text-gray-500 mb-1">
                      {chain.map((item: ChainItem, idx: number) => (
                        <span key={idx} className="inline-block">
                          {item.field}
                          {idx < chain.length - 1 && <i className="ri-arrow-left-s-line text-xs ml-1 text-primary">{typeof chain[idx + 1]?.compareMethod === 'object' ? '' : getCompareMethodText(chain[idx + 1]?.compareMethod)}<i className="ri-arrow-right-s-line mr-1 text-xs text-primary" /></i>}
                        </span>
                      ))}
                    </div>
                    <div className="flex flex-col">
                      {chain.map((item: ChainItem, idx: number) => (
                        <button key={`item_${idx}`} className="value-content p-1 cursor-text text-xs border-b border-dashed border-gray-200 last:border-b-0 text-left w-full rounded transition-colors"
                          onClick={(e) => { e.stopPropagation(); if (item.data.page) onReviewPointSelect(reviewPoint.id, Number(item.data.page), item.data.char_positions, item.data.value); else if (reviewPoint.contentPage && reviewPoint.contentPage[item.field]) onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[item.field]), item.data.char_positions, item.data.value); else toastService.error(`没有找到${item.field}对应的索引内容`); }}
                          type="button">
                          <div className="flex justify-between w-full">
                            <ReactTableTooltip content={item.data.value?.toString() || ''} />
                            {!item.data.page && !(reviewPoint.contentPage && reviewPoint.contentPage[item.field]) && <i className="ri-information-line text-red-500 text-xs" title="没有找到对应的文书内容" />}
                          </div>
                        </button>
                      ))}
                    </div>
                  </div>
                  <div className="status-indicator w-8 flex items-center justify-center">{res ? <i className="ri-check-line text-success text-base" /> : <i className="ri-alert-line text-warning text-base" />}</div>
                </div>
              </div>
            );
          }

          // Standard pair (2 elements)
          return (
            <div key={`pair_${ci}`} className={`${itemCls} border border-gray rounded-md overflow-hidden mb-2 ${res ? 'bg-[rgba(246,255,237,1)]' : 'bg-[rgba(255,251,230,1)]'} flex`}>
              <button className={`value-box hover:shadow-[0_0_10px_rgba(0,0,0,0.1)] flex-1 p-2 border-r-2 ${res ? 'border-green-200' : 'border-yellow-200'} text-left ${res ? 'hover:bg-[rgba(0,128,0,0.1)]' : 'hover:bg-[rgba(255,255,0,0.1)]'} transition-colors flex flex-col`}
                onClick={(e) => { e.stopPropagation(); if (chain[0].data.page) onReviewPointSelect(reviewPoint.id, chain[0].data.page, chain[0].data.char_positions, chain[0].data.value); else if (reviewPoint.contentPage && reviewPoint.contentPage[chain[0].field]) onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[chain[0].field]), chain[0].data.char_positions, chain[0].data.value); else toastService.error(`没有找到${chain[0].field}对应的索引内容`); }}
                type="button">
                <div className="value-source text-xs text-gray-500 mb-1">{chain[0].field}</div>
                <ReactTableTooltip content={chain[0].data.value?.toString() || ''} />
              </button>
              <button className={`value-box flex flex-col flex-1 p-2 text-left ${res ? 'hover:bg-[rgba(0,128,0,0.1)]' : 'hover:bg-[rgba(255,255,0,0.1)]'} transition-colors hover:shadow-[0_0_10px_rgba(0,0,0,0.1)]`}
                onClick={(e) => { e.stopPropagation(); if (chain[1].data.page) onReviewPointSelect(reviewPoint.id, chain[1].data.page, chain[1].data.char_positions, chain[1].data.value); else if (reviewPoint.contentPage && reviewPoint.contentPage[chain[1].field]) onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[chain[1].field]), chain[1].data.char_positions, chain[1].data.value); else toastService.error(`没有找到${chain[1].field}对应的索引内容`); }}
                type="button">
                <div className="value-source text-xs text-gray-500 mb-1">{chain[1].field}</div>
                <ReactTableTooltip content={chain[1].data.value?.toString() || ''} />
              </button>
              <div className="w-8 flex items-center justify-center border-l border-gray-200" onMouseEnter={(e) => { const r = e.currentTarget.getBoundingClientRect(); showTooltip(<div className="flex flex-row gap-2">{chain.slice(1).map((item: ChainItem, i: number) => <div key={i} className="rounded-md flex flex-row items-center"><div className="text-xs text-gray-600 pl-1 whitespace-nowrap">{typeof item.compareMethod === 'object' ? '' : `${getCompareMethodText(item.compareMethod)}:`}</div><div className="p-1 text-xs rounded-full min-w-[50px] text-center">{res ? '通过' : '不通过'}</div></div>)}</div>, { top: r.top + r.height / 2, left: r.left }); }} onMouseLeave={hideTooltip}>
                {res ? <i className="ri-check-line text-success text-base" /> : <i className="ri-alert-line text-warning text-base" />}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── renderModelRule ──
function RenderModelRule({ rule, reviewPoint, onReviewPointSelect, fileFormat }: { rule: Record<string, unknown>; reviewPoint: ReviewPoint; onReviewPointSelect: (id: string | number, page?: number, charPos?: CharPosition[], value?: string, bboxHighlight?: PdfBboxHighlight) => void; fileFormat?: string }) {
  const config = rule.config as { model?: string; fields?: Record<string, { page: number | string; value: string; char_positions?: CharPosition[]; res?: boolean }>; ai_suggestion?: { summary?: string; analysis?: { failure_reason?: string; solution_approach?: string; rule_understanding?: string }; suggestions?: Record<string, { reason: string; source: { page: number | null; type: string; field: string | null }; priority: string; confidence: number; suggested_value: string | null }>; generated_at?: string }; message?: string; res?: boolean } | undefined;

  if (config?.res !== reviewPoint.result) return null;
  if (!config) return null;

  const fieldElements: JSX.Element[] = [];
  if (config.fields) {
    Object.entries(config.fields).forEach(([key, value], index) => {
      const res = value.res !== undefined && value.res !== null ? value.res : value.value.trim() !== '';
      fieldElements.push(
        <button key={`field-${index}`} className={`border border-gray w-full rounded-md overflow-hidden mb-2 ${res ? 'bg-[rgba(246,255,237,1)]' : 'bg-[rgba(255,251,230,1)]'} flex hover:shadow-[0_0_10px_rgba(0,0,0,0.1)] ${res ? 'hover:bg-[rgba(0,128,0,0.1)]' : 'hover:bg-[rgba(255,255,0,0.1)]'}`}
          onClick={(e) => { e.stopPropagation(); if (value.page) onReviewPointSelect(reviewPoint.id, Number(value.page), value.char_positions, value.value); else if (reviewPoint.contentPage && reviewPoint.contentPage[key]) onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[key]), value.char_positions, value.value); else toastService.error(`没有找到${key}对应的索引内容`); }}
          type="button">
          <div className="p-1 flex-1 text-left">
            <div className="text-xs text-left text-gray-500 mb-1">{key}{!value.page && !(reviewPoint.contentPage && reviewPoint.contentPage[key]) && <i className="ri-information-line text-red-500 text-xs ml-1" />}{!res && !value.value && <span className="ml-2 text-xs text-yellow-500">缺失</span>}</div>
            {value.value && <ReactTableTooltip content={value.value} />}
          </div>
          <div className="w-8 flex items-center justify-center rounded-r-md" onMouseEnter={(e) => { const r = e.currentTarget.getBoundingClientRect(); showTooltip(<div className="flex flex-row gap-2"><div className="rounded-md flex flex-row items-center"><div className="text-xs text-gray-600 pl-1 whitespace-nowrap">大模型判断:</div><div className="p-1 text-xs rounded-full min-w-[50px] text-center">{res ? '通过' : '不通过'}</div></div></div>, { top: r.top + r.height / 2, left: r.left }); }} onMouseLeave={hideTooltip}>
            {res ? <i className="ri-check-line text-success text-base" /> : <i className="ri-alert-line text-warning text-base" />}
          </div>
        </button>
      );
    });
  }

  if (config.message) {
    const msg = typeof config.message === 'object' ? JSON.stringify(config.message) : String(config.message);
    fieldElements.push(
      <div key="message" className="mb-3 select-text">
        <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1">
          <i className="ri-sparkling-2-fill text-fuchsia-500 text-[12px]" /> AI 评查意见
        </div>
        <div className="p-2 bg-blue-50 rounded border border-blue-200 text-xs">
          <div className="flex flex-row items-center">
            <p className="text-xs text-gray-600 select-text mb-0">{msg}</p>
          </div>
        </div>
      </div>
    );
  }

  return <>{fieldElements}</>;
}

// ── Main Component ──


function stringifyUnknown(value: unknown): string {
  if (typeof value === 'string') return value;
  if (typeof value === 'number' || typeof value === 'boolean') return String(value);
  if (value == null) return '';
  try {
    return JSON.stringify(value, null, 2);
  } catch {
    return String(value);
  }
}

function getLeauditFieldText(value: ReviewPoint['content'][string]): string {
  if (value == null) return '未填写';
  if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
    return String(value);
  }
  if (typeof value === 'object' && value && 'value' in value) {
    return stringifyUnknown(value.value);
  }
  return stringifyUnknown(value);
}

function parseMissingArrayString(rawText: string): string[] {
  const match = rawText.match(/missing[\w-]*\s*:\s*\[([\s\S]*?)\]/i) || rawText.match(/\[([\s\S]*?)\]/);
  if (!match) return [];

  const innerText = match[1].trim();
  if (!innerText) return [];

  const quotedItems = Array.from(innerText.matchAll(/['"]([^'"]+)['"]/g))
    .map(item => item[1].trim())
    .filter(Boolean);

  if (quotedItems.length > 0) {
    return quotedItems;
  }

  return innerText
    .split(',')
    .map(item => item.trim().replace(/^['"]|['"]$/g, ''))
    .filter(Boolean);
}

function getLeauditMissingItems(reviewPoint: ReviewPoint): string[] {
  const textCandidates = [
    reviewPoint.skipReason,
    typeof reviewPoint.evaluatedPointResultsLog?.skip_reason === 'string' ? reviewPoint.evaluatedPointResultsLog.skip_reason : '',
    reviewPoint.suggestion,
  ].filter(Boolean) as string[];

  for (const text of textCandidates) {
    if (!/missing[\w-]*\s*:/i.test(text)) continue;
    const items = parseMissingArrayString(text);
    if (items.length > 0) return items;
  }

  return [];
}

function normalizeAiResponseItems(value: unknown, options?: { hideNone?: boolean }): string[] {
  const hideNone = options?.hideNone === true;

  if (Array.isArray(value)) {
    return value
      .map(item => String(item).trim())
      .filter(item => item && (!hideNone || item !== '无'));
  }

  if (typeof value === 'string') {
    const trimmed = value.trim();
    if (!trimmed) return [];
    if (hideNone && trimmed === '无') return [];
    return [trimmed];
  }

  return [];
}

function LeauditReviewPointDetailCard({ reviewPoint, onReviewPointSelect, onStatusChange }: {
  reviewPoint: ReviewPoint;
  onReviewPointSelect: (id: string | number, page?: number, charPos?: CharPosition[], value?: string, bboxHighlight?: PdfBboxHighlight) => void;
  onStatusChange: (id: string, editAuditStatusId: string | number, status: string, message: string) => void;
}) {
  const [manualNote, setManualNote] = useState(() => getLeauditNote(reviewPoint));
  const [corporateModalVisible, setCorporateModalVisible] = useState(false);
  const [corporateCompanyName, setCorporateCompanyName] = useState('');
  const [corporateLoading, setCorporateLoading] = useState(false);
  const [corporateBusinessInfo, setCorporateBusinessInfo] = useState<BusinessInfoResult | null>(null);
  const [corporateDishonestyInfo, setCorporateDishonestyInfo] = useState<DishonestyResult | null>(null);
  const [corporateError, setCorporateError] = useState<string | null>(null);
  const [corporateUpdatedAt, setCorporateUpdatedAt] = useState<string | null>(null);

  useEffect(() => {
    setManualNote(getLeauditNote(reviewPoint));
  }, [reviewPoint.id, reviewPoint.editAuditStatusMessage, reviewPoint.actionContent, reviewPoint.suggestion]);

  const stages = Array.isArray(reviewPoint.evaluatedPointResultsLog?.stages)
    ? (reviewPoint.evaluatedPointResultsLog.stages as Array<Record<string, unknown>>)
    : [];
  const missingItems = getLeauditMissingItems(reviewPoint);
  const legalBasisList = Array.isArray(reviewPoint.legalBasis)
    ? reviewPoint.legalBasis
    : reviewPoint.legalBasis?.articles?.map(item => typeof item === 'string' ? item : (item.name || item.content || stringifyUnknown(item))) || [];
  const riskLabelMap: Record<string, { cls: string; label: string }> = {
    high: { cls: 'bg-red-50 text-red-700 border-red-200', label: '高风险' },
    medium: { cls: 'bg-amber-50 text-amber-700 border-amber-200', label: '中风险' },
    low: { cls: 'bg-emerald-50 text-emerald-700 border-emerald-200', label: '低风险' },
  };
  const riskMeta = riskLabelMap[reviewPoint.riskLevel || ''] || {
    cls: 'bg-slate-100 text-slate-600 border-slate-200',
    label: '未知风险',
  };
  const configConfidence = reviewPoint.evaluationConfig && typeof reviewPoint.evaluationConfig === 'object'
    ? reviewPoint.evaluationConfig.confidence
    : undefined;
  const confidencePct = typeof reviewPoint.confidence === 'number'
    ? `${Math.round(reviewPoint.confidence * 100)}%`
    : typeof configConfidence === 'number'
      ? `${Math.round(configConfidence * 100)}%`
      : null;
  const isPass = reviewPoint.status === 'success' && reviewPoint.result === true;
  const isWarning = reviewPoint.status === 'warning' || (reviewPoint.ruleStatus || '').startsWith('skipped_');
  const statusChip = isPass
    ? { cls: 'bg-emerald-50 text-emerald-700 border-emerald-200', icon: 'ri-checkbox-circle-fill', label: '通过' }
    : isWarning
      ? { cls: 'bg-amber-50 text-amber-700 border-amber-200', icon: 'ri-error-warning-fill', label: '提醒' }
      : { cls: 'bg-red-50 text-red-700 border-red-200', icon: 'ri-close-circle-fill', label: '不通过' };
  const summaryText = isPass
    ? (reviewPoint.passMessage || reviewPoint.suggestion || '校验通过')
    : isWarning
      ? (reviewPoint.skipReason || reviewPoint.suggestion || '当前规则未执行或需人工关注')
      : (reviewPoint.failMessage || reviewPoint.suggestion || '发现问题，请处理');
  const partyANameRaw = getLeauditRawFieldValue(reviewPoint.content?.['甲方名称']);
  const partyBNameRaw = getLeauditRawFieldValue(reviewPoint.content?.['乙方名称']);
  const partyAName = typeof partyANameRaw === 'string' ? partyANameRaw.trim() : String(partyANameRaw || '').trim();
  const partyBName = typeof partyBNameRaw === 'string' ? partyBNameRaw.trim() : String(partyBNameRaw || '').trim();
  const shouldShowEnterpriseButtons = reviewPoint.groupName?.trim() === '合同主体';

  const handleCorporateInfoClick = async (companyName: string, forceRefresh = false) => {
    if (!companyName) {
      toastService.warning('企业名称为空，无法查询');
      return;
    }

    setCorporateModalVisible(true);
    setCorporateCompanyName(companyName);
    setCorporateLoading(true);
    setCorporateError(null);
    setCorporateBusinessInfo(null);
    setCorporateDishonestyInfo(null);
    setCorporateUpdatedAt(null);

    try {
      const response = await queryCompanyInfo({ keyword: companyName, force_refresh: forceRefresh });

      if (response.success && response.data) {
        setCorporateBusinessInfo(response.data.enterprise);
        setCorporateUpdatedAt(response.data.updated_at);
        if (response.data.dishonesty) {
          setCorporateDishonestyInfo({
            VerifyResult: response.data.dishonesty.VerifyResult,
            Data: response.data.dishonesty.Data || [],
          });
        }
      } else {
        setCorporateError(response.message || '查询失败');
      }
    } catch (error) {
      console.error('查询企业信息失败:', error);
      setCorporateError(error instanceof Error ? error.message : '查询失败');
    } finally {
      setCorporateLoading(false);
    }
  };

  const handleCorporateForceRefresh = async () => {
    if (corporateCompanyName) {
      await handleCorporateInfoClick(corporateCompanyName, true);
    }
  };

  const handleCloseCorporateModal = () => {
    setCorporateModalVisible(false);
    setCorporateCompanyName('');
    setCorporateBusinessInfo(null);
    setCorporateDishonestyInfo(null);
    setCorporateError(null);
    setCorporateUpdatedAt(null);
  };

  const renderFieldCard = (fieldKey: string, fieldValue: string) => {
    const page = getLeauditTargetPage(reviewPoint, fieldKey);
    const bboxHighlight = getLeauditBboxHighlight(reviewPoint, fieldKey, page);
    const enterpriseButton =
      shouldShowEnterpriseButtons && fieldKey === '甲方名称' && partyAName
        ? renderEnterpriseInfoButton('甲方企业信息', partyAName)
        : shouldShowEnterpriseButtons && fieldKey === '乙方名称' && partyBName
          ? renderEnterpriseInfoButton('乙方企业信息', partyBName)
          : null;
    return (
      <button
        key={fieldKey}
        type="button"
        className={`w-full border rounded-md text-left transition ${page ? 'hover:bg-[#f6ffed] hover:border-[#b7eb8f]' : 'opacity-90'} border-slate-200 bg-slate-50 field-btn`}
        onClick={() => {
          if (page) onReviewPointSelect(reviewPoint.id, page, undefined, fieldValue, bboxHighlight);
        }}
      >
        <div className="p-2.5 flex-1 min-w-0">
          <div className="flex items-center justify-between gap-2">
            <div className="flex items-center gap-2 min-w-0">
              <div className="inline-flex items-center text-[11px] text-slate-500 truncate font-medium">{fieldKey}</div>
              {enterpriseButton && enterpriseButton}
            </div>
            <div className="flex items-center gap-2 shrink-0">
              {page && <span className="text-[10.5px] text-slate-400 shrink-0">P{page}</span>}
            </div>
          </div>
          <div
            className="text-[12px] text-slate-700 mt-1 leading-relaxed whitespace-pre-wrap break-words select-text cursor-text"
            onClick={(e) => e.stopPropagation()}
            onMouseDown={(e) => e.stopPropagation()}
          >
            {fieldValue}
          </div>
        </div>
        {/* <div className="w-8 shrink-0 flex items-center justify-center border-l border-slate-200">
          <i className="ri-focus-3-line text-[#00684a] text-[16px]" />
        </div> */}
      </button>
    );
  };

  const renderEnterpriseInfoButton = (label: string, companyName: string) => (
    <button
      type="button"
      className={`inline-flex items-center gap-1 h-5 px-1.5 rounded border text-[10.5px] transition-colors flex-shrink-0 ${
        companyName
          ? 'bg-[#00684a] text-white border-[#00684a] hover:bg-[#005a3f] hover:border-[#005a3f]'
          : 'bg-slate-100 text-slate-400 border-slate-200 cursor-not-allowed'
      }`}
      disabled={!companyName}
      onClick={(e) => {
        e.stopPropagation();
        if (companyName) {
          void handleCorporateInfoClick(companyName);
        }
      }}
    >
      <i className="ri-building-4-line text-[11px]" />
      {label}
    </button>
  );

  const renderStageContent = (stage: Record<string, unknown>, index: number) => {
    const detail = (stage.detail || {}) as Record<string, unknown>;
    const checkType = String(stage.check_type || 'unknown');
    const passed = stage.passed === true;
    const hasPassedState = typeof stage.passed === 'boolean';
    const stageCardClass = passed ? 'border-emerald-200 bg-emerald-50/60' : 'border-slate-200 bg-slate-50/60';
    const stageBadgeClass = passed ? 'text-emerald-700' : 'text-slate-600';
    const stageLabelMap: Record<string, string> = {
      required: '字段必填',
      match: '一致性比对',
      ai: 'AI 评查',
      contains: '包含校验',
      compare: '比较校验',
    };
    const stageDisplayName = typeof stage.check_type_chinese === 'string' && stage.check_type_chinese.trim()
      ? stage.check_type_chinese.trim()
      : (stageLabelMap[checkType] || checkType);
    const stageReason = typeof stage.reason === 'string' ? stage.reason.trim() : '';

    const getStageDisplayValue = (value: unknown) => {
      if (value == null || value === '') return '—';
      if (typeof value === 'string') return value;
      if (typeof value === 'number' || typeof value === 'boolean') return String(value);
      return stringifyUnknown(value);
    };

    const renderStageInfoRow = (
      label: string,
      value: unknown,
      options?: { valueClassName?: string; mono?: boolean },
    ) => (
      <div className="px-2.5 py-2 flex items-start justify-between gap-3 border-t border-slate-100">
        <div className="text-[11px] text-slate-500 shrink-0">{label}</div>
        <div className={`text-[11px] text-slate-700 text-left break-words whitespace-pre-wrap max-w-[72%] ml-auto ${options?.mono ? 'font-mono' : ''} ${options?.valueClassName || ''}`}>
          {getStageDisplayValue(value)}
        </div>
      </div>
    );

    if (checkType === 'required') {
      const fields = Array.isArray(detail.fields) ? detail.fields.map(item => String(item)) : [];
      const missing = Array.isArray(detail.missing) ? detail.missing.map(item => String(item)) : [];
      return (
        <div key={`stage-${index}`} className={`border rounded-md p-3 ${stageCardClass}`}>
          <div className="flex items-center justify-between gap-2 mb-2">
            <div className="text-[11px] font-medium text-slate-500 uppercase tracking-wider">{stageDisplayName}</div>
            <span className={`text-[10.5px] inline-flex items-center gap-1 ${stageBadgeClass}`}>
              <i className={passed ? 'ri-checkbox-circle-fill' : 'ri-error-warning-fill'} />{passed ? '通过' : '缺失'}
            </span>
          </div>
          {fields.length > 0 && <div className="text-[12px] text-slate-700">{`命中字段：${fields.join('、')}`}</div>}
          {missing.length > 0 && <div className="text-[12px] text-amber-700 mt-1">{`缺失字段：${missing.join('、')}`}</div>}
        </div>
      );
    }

    if (checkType === 'match') {
      const failures = Array.isArray(detail.failures)
        ? detail.failures.map(item => item as Record<string, unknown>)
        : [];
      return (
        <div key={`stage-${index}`} className={`border rounded-md p-3 ${stageCardClass}`}>
          <div className="flex items-center justify-between gap-2 mb-2">
            <div className="text-[11px] font-medium text-slate-500 uppercase tracking-wider">{stageDisplayName}</div>
            <span className={`text-[10.5px] inline-flex items-center gap-1 ${stageBadgeClass}`}>
              <i className={passed ? 'ri-checkbox-circle-fill' : 'ri-error-warning-fill'} />{passed ? '通过' : '不一致'}
            </span>
          </div>
          {failures.length > 0 ? (
            <div className="space-y-2 mt-2">
              {failures.map((failure, failureIndex) => {
                const leftField = String(failure.a || '左侧字段');
                const rightField = String(failure.b || '右侧字段');
                const leftValue = failure.a_value == null ? '—' : String(failure.a_value);
                const rightValue = failure.b_value == null ? '—' : String(failure.b_value);

                return (
                  <div key={`failure-${index}-${failureIndex}`} className="border border-red-200 rounded-md bg-white/90 overflow-hidden">
                    <div className="px-3 py-1 flex items-center justify-between gap-2 border-b border-red-100 bg-red-50/70">
                      <div className="text-[11px] text-slate-500">{`差异项 ${failureIndex + 1}`}</div>
                    </div>
                    <div className="py-1">
                      <div className="px-2.5 py-1.5 flex items-start justify-between gap-3">
                        <div className="text-[11px] text-slate-500 max-w-[46%] basis-[46%] break-words whitespace-normal">{leftField}</div>
                        {failure.a_value == null ? (
                          <span className="text-[11px] text-red-600 inline-flex items-center justify-start gap-0.5 font-medium max-w-[46%] basis-[46%] whitespace-normal break-words text-left ml-auto">
                            <i className="ri-prohibited-line" />
                            {'未填写'}
                          </span>
                        ) : (
                          <div className={`text-[11px] text-slate-800 max-w-[46%] basis-[46%] text-left break-words whitespace-normal ml-auto ${/^\d/.test(leftValue) ? 'font-mono' : ''}`}>
                            {leftValue}
                          </div>
                        )}
                      </div>
                      <div className="px-2.5 py-1.5 flex items-start justify-between gap-3 border-t border-slate-100">
                        <div className="text-[11px] text-slate-500 max-w-[46%] basis-[46%] break-words whitespace-normal">{rightField}</div>
                        {failure.b_value == null ? (
                          <span className="text-[11px] text-red-600 inline-flex items-center justify-start gap-0.5 font-medium max-w-[46%] basis-[46%] whitespace-normal break-words text-left ml-auto">
                            <i className="ri-prohibited-line" />
                            {'未填写'}
                          </span>
                        ) : (
                          <div className={`text-[11px] text-slate-800 max-w-[46%] basis-[46%] text-left break-words whitespace-normal ml-auto ${/^\d/.test(rightValue) ? 'font-mono' : ''}`}>
                            {rightValue}
                          </div>
                        )}
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>
          ) : (
            <div className="text-[12px] text-slate-600">{'未发现不一致项'}</div>
          )}
        </div>
      );
    }

    if (checkType === 'compare') {
      const opMap: Record<string, string> = {
        '>=': '≥', '<=': '≤', '!=': '≠', '<>': '≠', '==': '=', '>': '>', '<': '<', '=': '=',
      };
      const displayOp = opMap[String(detail.op)] || String(detail.op);

      const fmtNum = (v: unknown) => {
        if (v == null || String(v).trim() === '') return getStageDisplayValue(v);
        const n = Number(v);
        return !isNaN(n) ? n.toLocaleString('zh-CN') : getStageDisplayValue(v);
      };

      const buildOperand = (field: unknown, value: unknown) => {
        const fieldStr = getStageDisplayValue(field);
        if (value == null || value === '') return fieldStr;
        return `${fieldStr}(${fmtNum(value)})`;
      };

      const leftOperand = buildOperand(detail.left, detail.left_value);
      const rightDisplay = typeof detail.right === 'number'
        ? fmtNum(detail.right)
        : buildOperand(detail.right, detail.right_value);

      return (
        <div key={`stage-${index}`} className={`border rounded-md p-3 ${stageCardClass}`}>
          <div className="flex items-center justify-between gap-2 mb-2">
            <div className="text-[11px] font-medium text-slate-500 uppercase tracking-wider">{stageDisplayName}</div>
            <span className={`text-[10.5px] inline-flex items-center gap-1 ${stageBadgeClass}`}>
              <i className={passed ? 'ri-checkbox-circle-fill' : 'ri-close-circle-fill'} />
              {passed ? '通过' : '未通过'}
            </span>
          </div>
          <div className={`text-[12px] font-mono ${passed ? 'text-slate-800' : 'text-red-800'}`}>
            {leftOperand} <span className="mx-1 font-bold">{displayOp}</span> {rightDisplay}
          </div>
          {stageReason && (
            <div className="text-[11px] text-slate-500 mt-1">{stageReason}</div>
          )}
        </div>
      );
    }

    if (checkType === 'ai') {
      const response = (detail.response || {}) as Record<string, unknown>;
      const reasonText = typeof response.reason === 'string' ? response.reason.trim() : '';
      const strengthItems = normalizeAiResponseItems(response.strengths);
      const suggestionItems = normalizeAiResponseItems(response.suggestion, { hideNone: true });
      const dividerClass = passed ? 'border-emerald-200/70' : 'border-fuchsia-200/70';

      return (
        <section key={`stage-${index}`} className="px-0 pt-0">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1">
            <i className="ri-sparkling-2-fill text-fuchsia-500 text-[12px]" />
            {'AI 评查意见'}
          </div>
          <div className={`p-3 border rounded-md space-y-3 ${passed ? 'bg-emerald-50/70 border-emerald-200' : 'bg-fuchsia-50/60 border-fuchsia-200'}`}>
            {reasonText && (
              <div className="flex gap-2 text-[12.5px] text-slate-700 leading-relaxed">
                <i className={`${passed ? 'ri-checkbox-circle-line text-emerald-500' : 'ri-error-warning-line text-fuchsia-500'} shrink-0 mt-0.5`} />
                <div className="whitespace-pre-wrap break-words">{reasonText}</div>
              </div>
            )}

            {strengthItems.length > 0 && (
              <div className={`pt-2 border-t ${dividerClass}`}>
                <div className="flex items-center gap-1 text-[11px] font-medium text-emerald-700 mb-1.5">
                  <i className="ri-medal-line" />
                  {'亮点'}
                  <span className="font-mono text-[10.5px] text-emerald-500">{strengthItems.length}</span>
                </div>
                <ul className="space-y-1 text-[12px] text-slate-700">
                  {strengthItems.map((item, itemIndex) => (
                    <li key={`strength-${index}-${itemIndex}`} className="flex gap-1.5">
                      <i className="ri-check-line text-emerald-500 mt-[1px] shrink-0" />
                      <span className="break-words">{item}</span>
                    </li>
                  ))}
                </ul>
              </div>
            )}

            {suggestionItems.length > 0 && (
              <div className={`pt-2 border-t ${dividerClass}`}>
                <div className="flex items-center gap-1 text-[11px] font-medium text-fuchsia-700 mb-1.5">
                  <i className="ri-edit-2-line" />
                  {'修改建议'}
                  <span className="font-mono text-[10.5px] text-fuchsia-500">{suggestionItems.length}</span>
                </div>
                <ul className="space-y-1 text-[12px] text-slate-700">
                  {suggestionItems.map((item, itemIndex) => (
                    <li key={`suggestion-${index}-${itemIndex}`} className="flex gap-1.5">
                      <span className="text-fuchsia-400 shrink-0">{'•'}</span>
                      <span className="break-words">{item}</span>
                    </li>
                  ))}
                </ul>
              </div>
            )}
          </div>
        </section>
      );
    }

    return (
      <div key={`stage-${index}`} className={`border rounded-md p-3 ${stageCardClass}`}>
        <div className="flex items-center justify-between gap-2 mb-2">
          <div className="text-[11px] font-medium text-slate-500 uppercase tracking-wider">
            {stageDisplayName}
          </div>
          {hasPassedState && (
            <span className={`inline-flex items-center gap-1 px-1.5 h-5 rounded text-[10.5px] border ${
              passed
                ? 'bg-emerald-50 text-emerald-700 border-emerald-200'
                : 'bg-red-50 text-red-700 border-red-200'
            }`}>
              <i className={passed ? 'ri-checkbox-circle-fill' : 'ri-close-circle-fill'} />
              {passed ? '通过' : '未通过'}
            </span>
          )}
        </div>
        <div className="rounded-md border border-slate-200 bg-white/80 overflow-hidden">
          {/* {renderStageInfoRow('阶段类型', stageDisplayName)} */}
          {/* {hasPassedState && renderStageInfoRow('结果', passed ? '通过' : '未通过', { valueClassName: passed ? 'text-emerald-700' : 'text-red-700' })} */}
          {stageReason && renderStageInfoRow('原因', stageReason)}
        </div>
      </div>
    );
  };

  return (
    <>
      <article className="bg-white border border-slate-200 rounded-lg shadow-sm overflow-hidden">
      <header className="px-4 pt-4 pb-3 border-b border-slate-100">
        <div className="flex items-center gap-2 flex-wrap">
          {reviewPoint.pointId && (
            <span className="font-mono text-[11px] px-1.5 py-0.5 rounded bg-slate-100 text-slate-600 shrink-0">
              {reviewPoint.pointId}
            </span>
          )}
          <h2 className="text-[14.5px] font-semibold text-slate-900 break-all leading-snug">{reviewPoint.pointName}</h2>
        </div>
        <div className="mt-2 flex items-center justify-between gap-3 flex-wrap">
          <div className="flex items-center gap-2 flex-wrap">
            <span className={`inline-flex items-center gap-1 px-1.5 h-5 rounded text-[10.5px] border ${statusChip.cls}`}>
              <i className={statusChip.icon} />{statusChip.label}
            </span>
            <span className={`inline-flex items-center gap-1 px-1.5 h-5 rounded text-[10.5px] border ${riskMeta.cls}`}>
              <i className="ri-focus-3-line" />{riskMeta.label}
            </span>
            {reviewPoint.postAction === 'manual' && (
              <span className="inline-flex items-center gap-1 px-1.5 h-5 rounded text-[10.5px] bg-slate-50 text-slate-600 border border-slate-200">
                <i className="ri-user-line" />需人工
              </span>
            )}
          </div>
          <div className="flex items-center gap-3 text-[11px] text-slate-500">
            {reviewPoint.score != null && <span>得分 <span className="font-mono text-slate-700">{reviewPoint.finalScore ?? reviewPoint.machineScore ?? reviewPoint.score}/{reviewPoint.score}</span></span>}
            {confidencePct && <span>置信度 <span className="font-mono text-slate-700">{confidencePct}</span></span>}
          </div>
        </div>
      </header>

      {Object.keys(reviewPoint.content || {}).length > 0 && (
        <section className="px-4 py-2">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2">
            命中字段 <span className="font-mono normal-case text-[10.5px]">{Object.keys(reviewPoint.content).length}</span>
          </div>
          <div className="space-y-2">
            {Object.entries(reviewPoint.content).map(([key, value]) => renderFieldCard(key, getLeauditFieldText(value)))}
          </div>
        </section>
      )}

      {missingItems.length > 0 && (
        <section className="px-4 py-2">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2">
            缺失项 <span className="font-mono normal-case text-[10.5px]">{missingItems.length}</span>
          </div>
          <div className="space-y-2">
            {missingItems.map(item => (
              <div key={item} className="w-full border border-red-200 rounded-md bg-red-50/60">
                <div className="p-2.5 flex items-start justify-between gap-2">
                  <div className="min-w-0 text-[12px] text-slate-700 leading-relaxed break-words">{item}</div>
                  <span className="text-[10.5px] text-red-600 shrink-0">缺失</span>
                </div>
              </div>
            ))}
          </div>
        </section>
      )}

      {stages.length > 0 && (
        <section className="px-4 py-2">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2">
            阶段结果 <span className="font-mono normal-case text-[10.5px]">{stages.length}</span>
          </div>
          <div className="space-y-2">
            {stages.map((stage, index) => renderStageContent(stage, index))}
          </div>
        </section>
      )}

      {!isWarning && (
        <section className="px-4 py-2">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1">
            <i className={isPass ? 'ri-shield-check-line text-emerald-500 text-[12px]' : 'ri-close-circle-line text-red-500 text-[12px]'} />
            {isPass ? '校验结果' : '问题说明'}
          </div>
          <div className={`p-3 rounded-md border text-[12.5px] leading-relaxed ${isPass ? 'bg-emerald-50/60 border-emerald-200 text-slate-700' : 'bg-red-50/60 border-red-200 text-slate-700'}`}>
            {summaryText}
          </div>
        </section>
      )}

      {legalBasisList.length > 0 && (
        <section className="px-4 py-2">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2">法律依据</div>
          <div className="flex flex-wrap gap-2">
            {legalBasisList.map((item, index) => (
              <span key={`${item}-${index}`} className="bg-[#e6f4ff] border border-[#91caff] rounded-full px-2 py-0.5 text-xs text-[#0958d9]">
                {item}
              </span>
            ))}
          </div>
        </section>
      )}

      {reviewPoint.postAction === 'manual' && (
        <section className="px-4 py-2">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2">审核意见</div>
          <textarea
            rows={2}
            placeholder="请输入审核意见..."
            className={`w-full p-2 border border-slate-200 rounded-md text-[12.5px] min-h-[56px] focus:outline-none focus:border-[#00684a] focus:ring-2 focus:ring-[#00684a]/15 resize-none placeholder:text-slate-400 ${reviewPoint.editAuditStatus !== 0 ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`}
            value={manualNote}
            onChange={(e) => setManualNote(e.target.value)}
            disabled={reviewPoint.editAuditStatus !== 0}
          />
        </section>
      )}

      {reviewPoint.postAction === 'manual' && reviewPoint.editAuditStatus === 0 && (
        <footer className="mt-3 px-4 py-3 flex items-center justify-end gap-2 border-t border-slate-100 bg-slate-50/60">
          <button type="button" className="h-8 px-3 rounded-md text-[12.5px] bg-[#1890ff] text-white hover:bg-blue-600 flex items-center gap-1 font-medium" onClick={() => {
            if (manualNote.trim() === '') { toastService.error('请输入审核意见'); return; }
            onStatusChange(reviewPoint.id, reviewPoint.editAuditStatusId || '', 'true', manualNote);
          }}>
            <i className="ri-check-line" />通过
          </button>
          <button type="button" className="h-8 px-3 rounded-md text-[12.5px] bg-[#f5222d] text-white hover:bg-red-600 flex items-center gap-1 font-medium shadow-sm" onClick={() => {
            if (manualNote.trim() === '') { toastService.error('请输入审核意见'); return; }
            onStatusChange(reviewPoint.id, reviewPoint.editAuditStatusId || '', 'false', manualNote);
          }}>
            <i className="ri-close-line" />不通过
          </button>
        </footer>
      )}

      {reviewPoint.postAction === 'manual' && reviewPoint.editAuditStatus !== 0 && (
        <footer className="mt-3 px-4 py-3 flex items-center justify-end border-t border-slate-100 bg-slate-50/60">
          <button type="button" className="h-8 px-3 rounded-md text-[12.5px] bg-purple-600 text-white hover:bg-purple-700 flex items-center gap-1 font-medium" onClick={() => {
            onStatusChange(reviewPoint.id, reviewPoint.editAuditStatusId || '', 'review', '');
          }}>
            <i className="ri-refresh-line" />重新审核
          </button>
        </footer>
      )}

        {isPass && reviewPoint.postAction !== 'manual' && (
          <footer className="px-4 py-3 flex items-center gap-2 border-t border-slate-100 bg-slate-50/60">
            <span className="text-[11px] text-emerald-700 inline-flex items-center gap-1">
              <i className="ri-verified-badge-fill" />已自动通过
            </span>
          </footer>
        )}
      </article>

      <CorporateInfoModal
        visible={corporateModalVisible}
        onClose={handleCloseCorporateModal}
        companyName={corporateCompanyName}
        businessInfo={corporateBusinessInfo}
        dishonestyInfo={corporateDishonestyInfo}
        businessLoading={corporateLoading}
        dishonestyLoading={corporateLoading}
        businessError={corporateError}
        dishonestyError={corporateError}
        updatedAt={corporateUpdatedAt}
        onForceRefresh={handleCorporateForceRefresh}
      />
    </>
  );
}

function LegacyReviewPointDetailCard({ reviewPoint, onReviewPointSelect, onStatusChange, fileFormat }: ReviewPointDetailCardProps) {
  const [manualNote, setManualNote] = useState(
    () => reviewPoint.editAuditStatusMessage || normalizeActionContent(reviewPoint.actionContent) || reviewPoint.suggestion || ''
  );

  // reviewPoint 切换时重置默认值
  useEffect(() => {
    setManualNote(reviewPoint.editAuditStatusMessage || normalizeActionContent(reviewPoint.actionContent) || reviewPoint.suggestion || '');
  }, [reviewPoint.id, reviewPoint.editAuditStatusMessage, reviewPoint.actionContent, reviewPoint.suggestion]);

  const otherRules = filterOtherRule(reviewPoint);
  const isPass = reviewPoint.result === true;
  const isFail = reviewPoint.result === false && reviewPoint.status === 'error';
  const isWarn = reviewPoint.result === false && (reviewPoint.status === 'warning' || reviewPoint.status === 'info');

  const statusChip = isPass
    ? { cls: 'bg-emerald-50 text-emerald-700 border-emerald-200', icon: 'ri-checkbox-circle-fill', label: '通过' }
    : isFail
      ? { cls: 'bg-red-50 text-red-700 border-red-200', icon: 'ri-close-circle-fill', label: '不通过' }
      : isWarn
        ? { cls: 'bg-amber-50 text-amber-700 border-amber-200', icon: 'ri-lightbulb-flash-fill', label: '警告' }
        : { cls: 'bg-slate-100 text-slate-600 border-slate-200', icon: 'ri-forbid-2-line', label: '未涉及' };

  return (
    <article className="bg-white border border-slate-200 rounded-lg shadow-sm overflow-hidden">
      <TooltipPortal />

      {/* Header */}
      <header className="px-4 pt-4 pb-3 border-b border-slate-100">
        <div className="flex items-center gap-2 flex-wrap">
          {reviewPoint.pointId && (
            <span className="font-mono text-[11px] px-1.5 py-0.5 rounded bg-slate-100 text-slate-600 shrink-0">
              #{reviewPoint.pointId}
            </span>
          )}
          <h2 className="text-[14.5px] font-semibold text-slate-900 break-all leading-snug">
            {reviewPoint.pointName}
          </h2>
        </div>
        <div className="mt-2 flex items-center justify-between">
          <div className="flex items-center gap-2 flex-wrap">
            <span className={`inline-flex items-center gap-1 px-1.5 h-5 rounded text-[10.5px] border ${statusChip.cls}`}>
              <i className={statusChip.icon} />{statusChip.label}
            </span>
            { reviewPoint.postAction === 'manual' && (
              <span className="inline-flex items-center gap-1 px-1.5 h-5 rounded text-[10.5px] bg-slate-50 text-slate-600 border border-slate-200">
                <i className="ri-user-line" />需人工
              </span>
            )}
          </div>
          {reviewPoint.score != null && (
            <span className="text-[11px] text-slate-500 shrink-0">
              分值 <span className={`font-mono font-medium ${isPass ? 'text-emerald-600' : isFail ? 'text-red-600' : isWarn ? 'text-amber-600' : 'text-slate-500'}`}>{reviewPoint.score}</span>
            </span>
          )}
        </div>
      </header>

      {/* Rule content */}
      <section className="px-4 pt-3">
        {otherRules.map((rule, i) => (
          <RenderOtherRule key={`other-${i}`} rule={rule} reviewPoint={reviewPoint} onReviewPointSelect={onReviewPointSelect} />
        ))}
        {reviewPoint.evaluatedPointResultsLog?.rules?.map((rule, i) => {
          if (rule.type === 'consistency') {
            return <div key={`rule-${i}`}>{otherRules.length > 0 && <div className="bg-gray-50 rounded border border-gray-200 text-xs mb-3" />}<RenderConsistencyRule rule={rule} reviewPoint={reviewPoint} onReviewPointSelect={onReviewPointSelect} /></div>;
          }
          if (rule.type === 'ai') {
            return <div key={`rule-${i}`}>{otherRules.length > 0 && <div className="bg-gray-50 rounded border border-gray-200 text-xs mb-3" />}<RenderModelRule rule={rule} reviewPoint={reviewPoint} onReviewPointSelect={onReviewPointSelect} fileFormat={fileFormat} /></div>;
          }
          return null;
        })}
      </section>

      {/* Suggestion */}
      {reviewPoint.suggestion && !isPass && (
        <section className="px-4 pt-3">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-1">
            <i className="ri-edit-2-line" /> 修改建议
          </div>
          <div className="p-3 bg-amber-50/50 border border-amber-200 rounded-md text-[12.5px] text-slate-700 leading-relaxed">
            {reviewPoint.suggestion}
          </div>
        </section>
      )}

      {/* Manual review textarea */}
      {reviewPoint.postAction === 'manual' && (
        <section className="px-4 pt-3">
          <div className="text-[11px] font-medium text-slate-400 uppercase tracking-wider mb-2">审核意见</div>
          <textarea
            rows={2}
            placeholder="请输入审核意见..."
            className={`w-full p-2 border border-slate-200 rounded-md text-[12.5px] min-h-[56px] focus:outline-none focus:border-[#00684a] focus:ring-2 focus:ring-[#00684a]/15 resize-none placeholder:text-slate-400 ${reviewPoint.editAuditStatus !== 0 ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`}
            value={manualNote}
            onChange={(e) => setManualNote(e.target.value)}
            disabled={reviewPoint.editAuditStatus !== 0}
          />
        </section>
      )}

      {/* Footer actions — 需人工 + 未审核：通过/不通过 */}
      {reviewPoint.postAction === 'manual' && reviewPoint.editAuditStatus === 0 && (
        <footer className="mt-3 px-4 py-3 flex items-center justify-end gap-2 border-t border-slate-100 bg-slate-50/60">
          <button type="button" className="h-8 px-3 rounded-md text-[12.5px] bg-[#1890ff] text-white hover:bg-blue-600 flex items-center gap-1 font-medium" onClick={() => {
            if (manualNote.trim() === '') { toastService.error('请输入审核意见'); return; }
            onStatusChange(reviewPoint.id, reviewPoint.editAuditStatusId || '', 'true', manualNote);
          }}>
            <i className="ri-check-line" />通过
          </button>
          <button type="button" className="h-8 px-3 rounded-md text-[12.5px] bg-[#f5222d] text-white hover:bg-red-600 flex items-center gap-1 font-medium shadow-sm" onClick={() => {
            if (manualNote.trim() === '') { toastService.error('请输入审核意见'); return; }
            onStatusChange(reviewPoint.id, reviewPoint.editAuditStatusId || '', 'false', manualNote);
          }}>
            <i className="ri-close-line" />不通过
          </button>
        </footer>
      )}

      {/* 已审核状态：重新审核 */}
      {reviewPoint.postAction === 'manual' && reviewPoint.editAuditStatus !== 0 && (
        <footer className="mt-3 px-4 py-3 flex items-center justify-end border-t border-slate-100 bg-slate-50/60">
          <button type="button" className="h-8 px-3 rounded-md text-[12.5px] bg-purple-600 text-white hover:bg-purple-700 flex items-center gap-1 font-medium" onClick={() => {
            onStatusChange(reviewPoint.id, reviewPoint.editAuditStatusId || '', 'review', '');
          }}>
            <i className="ri-refresh-line" />重新审核
          </button>
        </footer>
      )}

      {isPass && reviewPoint.postAction !== 'manual' && (
        <footer className="px-4 py-3 flex items-center gap-2 border-t border-slate-100 bg-slate-50/60">
          <span className="text-[11px] text-emerald-700 inline-flex items-center gap-1">
            <i className="ri-verified-badge-fill" />已自动通过
          </span>
        </footer>
      )}
    </article>
  );
}

export function ReviewPointDetailCard(props: ReviewPointDetailCardProps) {
  if (props.detailMode === 'leaudit') {
    return (
      <LeauditReviewPointDetailCard
        reviewPoint={props.reviewPoint}
        onReviewPointSelect={props.onReviewPointSelect}
        onStatusChange={props.onStatusChange}
      />
    );
  }

  return <LegacyReviewPointDetailCard {...props} />;
}
