// [W6 S187 · YC-023 GĐ3] MÀN DUYỆT V2-TRẠM CỦA HỢP ĐỒNG.
// File MIRROR SHA256 identical với fe-admin counterpart.
//
// KHUÔN NGUỒN: `components/khkk/KhkkWorkflowPanel.tsx` (bản owner-trim S175 — CHỈ
// Duyệt / Trả lại). Cách suy trạng thái bê nguyên: BE trả CÂY THÔ + CẶP CON-TRỎ, panel
// tự suy Done/Current/Pending — KHÔNG có `status` precompute như cặp DTO của Duyệt NCC:
//   • `currentWorkflowStepIndex` = INDEX 0-based vào mảng `workflowSteps` (đã sort Order)
//   • `currentApprovalLevelOrder` = GIÁ TRỊ `level.order` (KHÔNG phải index)
// Luật so khớp OR-of-N canonical (BE `ContractWorkflowService.cs:264-279`):
// `steps[idx].levels.filter(l => l.order === cur)` rồi `.some(l => l.approverUserId === uid)`
// — N Cấp CÙNG order = OR-of-N, chỉ 1 người ký là tiến. Admin bypass (BE `:268`).
//
// ══ WIRE: KHÔNG CÓ "ENDPOINT V2" RIÊNG ═══════════════════════════════════════════════
// Chỉ có MỘT cửa: `POST /contracts/{id}/transitions` body `{targetPhase, decision, comment}`
// (`ContractsController.cs:64-69` + `:244`). `ApproveV2Async` là **private**, tới được bằng
// DISPATCH trong `TransitionAsync`: `:105` bắt `fromPhase==ChoDuyet && decision==Approve`,
// `:111` `if (contract.ApprovalWorkflowId is Guid awId)` → nhánh V2. Vậy nên:
//   • DUYỆT  → `{targetPhase: ChoDuyet(10), decision: Approve(1)}`. Nhánh approve KHÔNG đọc
//     `targetPhase` (BE tự tính: lên Cấp → lên Bước → `DaPhatHanh` + gen mã HĐ RG-001);
//     vẫn phải gửi giá trị HỢP LỆ vì validator `IsInEnum()` (`ContractFeatures.cs:210`).
//     Gửi `ChoDuyet` = trung tính, không hứa hộ BE một phase đích.
//   • TRẢ LẠI → `{targetPhase: TraLai(98), decision: Reject(2)}`. Nhánh reject (`:49-66`)
//     phân 2 ngả bằng ĐÚNG `targetPhase`: `== TuChoi` ⇒ TuChoi, else ⇒ TraLai. Gửi 98 tường
//     minh thay vì "số nào cũng được miễn khác 99".
//
// ══ [WQ3b S190 · YC-030 Q3] 3 MẢNH KHUÔN KHKK — TỪ CHỐI @S187, PORT ĐỦ ĐỢT NÀY ════════
// 🧊 Bản S187 liệt 3 mảnh này là "CỐ Ý KHÔNG PORT" và lý do khi đó ĐÚNG: cả ba đều thiếu
// máy ở BE. WQ3-BE (cùng phiên S190) dựng xong máy ⇒ 3 lý do đó HẾT HIỆU LỰC, không phải
// bị bỏ qua. Ghi lại lineage để người sau đọc panel này không tưởng ai đó lờ mất chú thích:
//  1. ✅ Ô-tích "Cấp này KẾT THÚC quy trình" — `TransitionContractBody` nay có
//        `ApplyLevelFinalize`/`SkipToFinal` (`ContractsController.cs:269-274`), `?? true` ở
//        `:71`. Trước đây vẽ ô-tích là control CHẾT; nay tích/bỏ-tích đổi thật đường đi.
//        🔴 Ô-tích là OPT-OUT: BE cần CẢ HAI vế `AllowApproverFinalize ∧ applyLevelFinalize`
//        (`ContractWorkflowService.cs:527`), mà bỏ trống ⇒ `true`. Vì vậy panel gửi giá trị
//        TƯỜNG MINH mỗi lần duyệt, không dựa vào mặc định của server.
//  2. ✅ Badge cho Cấp có `allowApproverFinalize` — `ApproveV2Async` NAY ĐỌC cờ đó (`:527`)
//        ⇒ câu "duyệt ở đây là KẾT THÚC" không còn nói dối. CHỮ trên badge KHÔNG chép nguyên
//        KHKK ("⚑ Duyệt thay CEO"): quy trình HĐ không có trạm CEO nào để "thay" — terminal
//        của HĐ là `DaPhatHanh` + gen mã RG-001 (`:392`). Dùng chữ đúng vật: "⚑ Cấp kết thúc".
//  3. ✅ Banner kết-thúc-sớm + meta `{code} v{NN}` — `ContractDetail` nay có
//        `workflowCode`/`workflowVersion`/`endedByLevelFinalize` (`ContractDtos.cs:103-105`),
//        không phải mượn `c.workflow.policyName` của V1 nữa. ⚠️ VẪN THIẾU so với KHKK:
//        `workflowName` + `finalizeStepName`/`finalizeLevelName` — BE HĐ không trả 3 field đó
//        ⇒ meta chỉ `{code} v{NN}`, còn tên Bước/Cấp kết-thúc panel TỰ SUY từ ý-kiến ký gần
//        nhất (xem chỗ dựng `finalizeMark`), KHÔNG bịa field và KHÔNG gọi thêm endpoint thứ 3.
//
// ══ 2 KHỐI BỎ VÌ TRÙNG CHỖ ĐÃ CÓ TRÊN CÙNG TRANG ════════════════════════════════════
// Cả 4 host (`ContractDetailPage` ×2 app · `MyContractsPage` · `ContractsListPage` ·
// `InboxPage`) đều render `ContractDetailContent` KÈM `WorkflowHistoryPanel`, mà panel đó đã
// có "Lịch sử duyệt (N)" + "Lịch sử điều chỉnh". Đính kèm cũng đã có
// `ContractAttachmentsSection` ngay trong `ContractDetailContent` (HĐ không có đường
// `approval-attachments` riêng như KHKK). ⇒ panel này CHỈ gồm: header · sơ đồ Bước→Cấp ·
// banner "đến lượt ai" · 2 nút + dialog.
import { useMemo, useState } from 'react'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { Button } from '@/components/ui/Button'
import { Dialog } from '@/components/ui/Dialog'
import { Label } from '@/components/ui/Label'
import { Textarea } from '@/components/ui/Textarea'
import { api } from '@/lib/api'
import { getErrorMessage } from '@/lib/apiError'
import { cn } from '@/lib/cn'
import { useAuth } from '@/contexts/AuthContext'
import {
  ApprovalDecision,
  ContractPhase,
  type ContractDetail,
  type ContractLevelOpinion,
  type ContractWorkflowLevel,
  type TransitionContractBody,
} from '@/types/contracts'

type NodeStatus = 'Done' | 'Current' | 'Pending'

/** 2 hành động owner chốt @S188 (84). "Từ chối" KHÔNG render — máy BE (`TuChoi=99`) giữ nguyên. */
type PanelAction = 'approve' | 'return'

function formatDateTime(iso: string): string {
  return new Date(iso).toLocaleString('vi-VN')
}

// Pattern 14 (Tailwind JIT): chuỗi class ĐẦY ĐỦ, KHÔNG nội suy mảnh (`bg-${x}-50` bị purge).
const STATUS_DOT: Record<NodeStatus, string> = {
  Done: 'bg-emerald-500 text-white',
  Current: 'bg-brand-600 text-white',
  Pending: 'bg-slate-200 text-slate-500',
}
const STATUS_BOX: Record<NodeStatus, string> = {
  Done: 'border-emerald-200 bg-emerald-50/40',
  Current: 'border-brand-300 bg-brand-50/50',
  Pending: 'border-slate-200 bg-white',
}
const STATUS_ICON: Record<NodeStatus, string> = {
  Done: '✓',
  Current: '●',
  Pending: '○',
}

export function ContractWorkflowPanel({
  contract: c,
  onChanged,
}: {
  contract: ContractDetail
  /** gọi sau khi duyệt/trả lại thành công — host refetch thêm theo nhu cầu của nó. */
  onChanged?: () => void
}) {
  const qc = useQueryClient()
  const { user } = useAuth()
  const [action, setAction] = useState<PanelAction | null>(null)
  const [comment, setComment] = useState('')
  // [WQ3b S190] Mặc định KHÔNG tick — khuôn KHKK (`KhkkWorkflowPanel.tsx:103`, vốn theo
  // `PeWorkflowPanel.tsx:61`). Người duyệt phải chủ động chọn "dừng ở đây"; mặc-định-tick là
  // đổi hành vi im lặng của mọi Cấp được uỷ quyền.
  const [applyLevelFinalize, setApplyLevelFinalize] = useState(false)

  const steps = c.workflowSteps ?? []
  const curStepIdx = c.currentWorkflowStepIndex
  const curLevelOrder = c.currentApprovalLevelOrder

  const isWaiting = c.phase === ContractPhase.ChoDuyet
  // Terminal thành công của HĐ là `DaPhatHanh` (kèm gen mã RG-001) — KHÔNG phải `DaDuyet`
  // như Duyệt NCC / KHKK (`ContractWorkflowService.cs:392`).
  const isIssued = c.phase === ContractPhase.DaPhatHanh

  // Chữ ký thật của từng Cấp: tra `levelOpinions` — KHÔNG tra `approvals` như khuôn KHKK.
  // 🔴 `ContractApproval` (`types/contracts.ts:103-112`) KHÔNG mang `approvalWorkflowLevelId`
  // (BE nhét "[Bước n — Cấp m]" vào chuỗi `comment` — `ContractWorkflowService.cs:291`), nên
  // đọc từ đó là phải parse chuỗi. `ContractLevelOpinion` mang thẳng khoá Level và được
  // `ApproveV2Async` UPSERT ngay trong cùng giao dịch duyệt (`:305-329`) ⇒ đúng nguồn.
  const signedByLevelId = useMemo(() => {
    const m = new Map<string, ContractLevelOpinion>()
    for (const o of c.levelOpinions ?? []) {
      if (!m.has(o.approvalWorkflowLevelId)) m.set(o.approvalWorkflowLevelId, o)
    }
    return m
  }, [c.levelOpinions])

  const stepStatus = (idx: number): NodeStatus => {
    if (isIssued) return 'Done'
    if (!isWaiting || curStepIdx == null) return 'Pending'
    if (idx < curStepIdx) return 'Done'
    if (idx === curStepIdx) return 'Current'
    return 'Pending'
  }

  const levelStatus = (idx: number, order: number): NodeStatus => {
    const s = stepStatus(idx)
    if (s !== 'Current' || curLevelOrder == null) return s
    if (order < curLevelOrder) return 'Done'
    if (order === curLevelOrder) return 'Current'
    return 'Pending'
  }

  // 🔴 OR-of-N: mọi Cấp CÙNG `order` trong Bước hiện tại đều là người được duyệt lượt này.
  const inRange = curStepIdx != null && curStepIdx >= 0 && curStepIdx < steps.length
  const currentStep = isWaiting && inRange ? steps[curStepIdx!] : null
  const currentLevels: ContractWorkflowLevel[] =
    currentStep != null && curLevelOrder != null
      ? currentStep.levels.filter(l => l.order === curLevelOrder)
      : []

  const isAdmin = user?.roles?.includes('Admin') ?? false
  const actorIsCurrentApprover = !!user?.id && currentLevels.some(l => l.approverUserId === user.id)
  const actorInLevel = isAdmin || actorIsCurrentApprover
  const blockedByLevel = isWaiting && !actorInLevel

  // [WQ3b S190] Ô-tích phải soi cờ của ĐÚNG Cấp mà BE sẽ chấm, không phải "có Cấp nào trong
  // nhóm bật cờ". Mirror `matchingLevel` (`ContractWorkflowService.cs:442-443`): own-level
  // TRƯỚC, Admin không có own ⇒ phần tử ĐẦU nhóm cùng-order. Soi cờ của Cấp khác là hiện một
  // ô-tích nói dối — tích xong BE vẫn trình tiếp vì cờ ở Cấp bị chấm là `false`.
  const actingLevel = currentLevels.find(l => l.approverUserId === user?.id)
    ?? (isAdmin ? currentLevels[0] : undefined)
  const approverFinalizeEligible = actingLevel?.allowApproverFinalize === true

  // [WQ3b S190] Tên Bước/Cấp đã KẾT THÚC sớm — DTO HĐ không có `finalizeStepName`/
  // `finalizeLevelName` như KHKK (`ContractDtos.cs:103-105` chỉ 3 field). Suy từ ý-kiến ký
  // GẦN NHẤT: `ApproveV2Async` UPSERT opinion NGAY TRƯỚC nhánh finalize trong cùng giao dịch
  // (`:435-529`) ⇒ chữ ký cuối cùng chính là Cấp đã bấm kết thúc. Không suy được (0 ý kiến,
  // phiếu cũ) ⇒ để `null` và banner nói câu chung, KHÔNG bịa tên.
  const finalizeMark = useMemo(() => {
    const ops = c.levelOpinions ?? []
    if (ops.length === 0) return null
    const last = [...ops].sort((a, b) => Date.parse(b.signedAt) - Date.parse(a.signedAt))[0]
    return { stepOrder: last.stepOrder, stepName: last.stepName, levelOrder: last.levelOrder, levelName: last.levelName }
  }, [c.levelOpinions])

  const transition = useMutation({
    mutationFn: async (a: PanelAction) => {
      // Xem khối WIRE ở đầu file: cặp (targetPhase, decision) LÀ thứ chọn nhánh trong
      // `TransitionAsync`, không phải một tên endpoint riêng.
      // [WQ3b S190 · YC-030 Q3] `applyLevelFinalize` CHỈ gắn ở nhánh duyệt, và GẮN TƯỜNG MINH
      // kể cả khi Cấp không được uỷ quyền: bỏ trống ⇒ controller `?? true`
      // (`ContractsController.cs:71`), tức im lặng = ĐỒNG Ý kết thúc sớm. Gửi
      // `approverFinalizeEligible ? applyLevelFinalize : true` = đúng khuôn KHKK
      // (`KhkkWorkflowPanel.tsx:199`): Cấp không bật cờ thì vế BE `AllowApproverFinalize`
      // đã `false` nên giá trị này vô hại; Cấp có bật thì đi theo đúng ô-tích người dùng chọn.
      const body: TransitionContractBody = a === 'approve'
        ? {
          targetPhase: ContractPhase.ChoDuyet,
          decision: ApprovalDecision.Approve,
          comment: comment.trim() || null,
          applyLevelFinalize: approverFinalizeEligible ? applyLevelFinalize : true,
        }
        : { targetPhase: ContractPhase.TraLai, decision: ApprovalDecision.Reject, comment: comment.trim() || null }
      await api.post(`/contracts/${c.id}/transitions`, body)
    },
    onSuccess: (_res, a) => {
      toast.success(a === 'approve' ? 'Đã duyệt cấp hiện tại.' : 'Đã trả lại người soạn sửa.')
      setAction(null)
      setComment('')
      // Trả ô-tích về mặc định. (Khuôn KHKK `:215-220` KHÔNG reset cờ này — với HĐ thì tick còn
      // sót là nguy: duyệt Cấp 1 có tick nhưng cấp đó không được uỷ quyền ⇒ BE bỏ qua, sang Cấp 2
      // ĐƯỢC uỷ quyền thì hộp thoại mở ra đã sẵn tick, kết thúc sớm mà người bấm không chủ ý.)
      setApplyLevelFinalize(false)
      // `['contract', id]` = queryKey detail của CẢ 4 host (`ContractDetailPage` ×2 app,
      // `MyContractsPage`, `ContractsListPage`, `InboxPage`). 3 key còn lại là prefix của
      // các danh sách — TanStack khớp theo tiền tố nên không cần liệt đủ tham số lọc.
      qc.invalidateQueries({ queryKey: ['contract', c.id] })
      qc.invalidateQueries({ queryKey: ['my-contracts'] })
      qc.invalidateQueries({ queryKey: ['contracts'] })
      qc.invalidateQueries({ queryKey: ['inbox'] })
      qc.invalidateQueries({ queryKey: ['contract-changelogs', c.id] })
      qc.invalidateQueries({ queryKey: ['pipeline-contract-index'] })
      onChanged?.()
    },
    onError: (e) => toast.error(getErrorMessage(e)),
  })

  const commentRequired = action === 'return'
  const confirmDisabled = transition.isPending || (commentRequired && !comment.trim())

  // Câu giải thích khi nút bị khoá. 🔴 [S182 MAJOR-1] KHÔNG đặt `title` lên chính
  // `<button disabled>`: Chromium nuốt sự kiện chuột của form-control disabled ⇒ tooltip
  // KHÔNG BAO GIỜ bật. Cắm lên `<span>` bọc ngoài (không disabled). Và thông tin PHÂN BIỆT
  // không nằm sau hover — banner "đến lượt ai" ngay trên đã nói bằng chữ nhìn-thấy-được.
  const blockedHint = 'Cấp hiện tại chỉ người được phân công (hoặc Admin) mới thao tác được.'

  return (
    <div className="space-y-4">
      {/* ── 1. Header + meta {code} v{NN} ───────────────────────────────────── */}
      <div>
        <h3 className="text-sm font-semibold text-slate-900">Quy trình duyệt</h3>
        {/* [WQ3b S190] Mã + phiên bản quy trình ĐANG PIN — khuôn `KhkkWorkflowPanel.tsx:286-289`.
            🔴 `font-mono` vì đây là MÃ (`HD-TP`…), và `padStart(2,'0')` cho `v01` ≠ `v1` để
            người đọc so nhanh với bản Designer. KHÔNG có `workflowName` như KHKK — BE HĐ không
            trả field đó (xem khối lineage đầu file), nên dòng này dừng ở mã + phiên bản. */}
        {c.workflowCode && (
          <p className="mt-0.5 font-mono text-[11px] text-slate-500">
            {c.workflowCode}
            {c.workflowVersion != null && ` v${String(c.workflowVersion).padStart(2, '0')}`}
          </p>
        )}
        <p className="mt-0.5 text-[11px] text-slate-500">
          Bước = Phòng · Cấp = nhân viên duyệt. Nhiều nhân viên cùng một Cấp thì chỉ cần MỘT
          người duyệt là sang Cấp kế.
        </p>
      </div>

      {/* ── 1-bis. Banner kết-thúc-sớm ──────────────────────────────────────── */}
      {/* [WQ3b S190 · YC-030 Q3] Cờ RUNTIME `endedByLevelFinalize` (BE `:529`) — chỉ bật khi
          việc ĐÃ xảy ra, nên câu ở thì QUÁ KHỨ. Nếu không có banner này, HĐ kết thúc sớm trông
          y hệt HĐ… bị bỏ dở: cây còn 2-3 Bước ○ chưa ai ký mà phiếu lại `Đã phát hành`.
          Tông EMERALD khuôn KHKK (`:299`) = "kết thúc hợp lệ", không dùng violet/amber. */}
      {c.endedByLevelFinalize && (
        <div className="rounded-md border border-emerald-300 bg-emerald-50 px-3 py-2 text-[12px] text-emerald-800">
          ✅ Quy trình đã KẾT THÚC sớm tại cấp được uỷ quyền — không trình tiếp
          {finalizeMark && (
            <> (Bước {finalizeMark.stepOrder}
              {finalizeMark.stepName ? ` — ${finalizeMark.stepName}` : ''} · Cấp {finalizeMark.levelOrder}
              {finalizeMark.levelName ? ` — ${finalizeMark.levelName}` : ''})</>
          )}
          . Các Bước/Cấp phía sau vẽ mờ vì không ai phải duyệt.
        </div>
      )}

      {/* ── 2. Sơ đồ Bước → Cấp → NV ────────────────────────────────────────── */}
      {steps.length > 0 ? (
        <ol className="space-y-2">
          {steps.map((step, idx) => {
            const st = stepStatus(idx)
            // Gom Cấp CÙNG `order` thành MỘT dòng — OR-of-N: N người, chỉ 1 chữ ký là tiến.
            const orders = [...new Set(step.levels.map(l => l.order))].sort((a, b) => a - b)
            return (
              <li key={step.id} className={cn('rounded-md border p-2', STATUS_BOX[st])}>
                <div className="flex flex-wrap items-center gap-1.5 text-xs font-medium">
                  <span className={cn('flex h-5 w-5 shrink-0 items-center justify-center rounded-full font-bold', STATUS_DOT[st])}>
                    {STATUS_ICON[st]}
                  </span>
                  <span className="text-slate-800">Bước {step.order} — {step.name}</span>
                  {step.departmentName && (
                    <span className="rounded bg-emerald-50 px-1.5 py-0.5 text-[10px] font-medium text-emerald-700">
                      {step.departmentName}
                    </span>
                  )}
                </div>
                {orders.length > 0 && (
                  <ul className="ml-7 mt-1.5 space-y-1 border-l-2 border-violet-200 pl-2">
                    {orders.map(order => {
                      const group = step.levels.filter(l => l.order === order)
                      // [WQ3b S190] Cấp được uỷ quyền kết thúc — cờ CẤU HÌNH (khác cờ runtime
                      // `endedByLevelFinalize` ở banner trên).
                      const finalizeHere = group.some(l => l.allowApproverFinalize)
                      const hasSigned = group.some(l => signedByLevelId.has(l.id))
                      // Nhánh KHÔNG CHẠY: HĐ đã phát hành bằng đường kết-thúc-sớm ⇒ các Cấp sau
                      // đó không ai ký. Vẽ MỜ + giữ trạng thái ○ thay vì bịa ✓ cho cả cây
                      // (`stepStatus` trả 'Done' cho MỌI Bước khi `isIssued`).
                      const skipped = isIssued && c.endedByLevelFinalize === true && !hasSigned
                      const ls: NodeStatus = skipped ? 'Pending' : levelStatus(idx, order)
                      return (
                        <li key={`${step.id}-${order}`} className={cn('text-[11px]', skipped && 'opacity-50')}>
                          <div className="flex items-start gap-1.5">
                            <span className={cn('mt-0.5 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[9px] font-bold', STATUS_DOT[ls])}>
                              {STATUS_ICON[ls]}
                            </span>
                            <div className="min-w-0 flex-1">
                              <div className="font-medium text-slate-700">
                                {group[0]?.name || `Cấp ${order}`}
                                {ls === 'Current' && <span className="ml-1.5 text-[10px] font-normal text-brand-700">đang chờ</span>}
                                {ls === 'Done' && <span className="ml-1.5 text-[10px] font-normal text-emerald-600">đã duyệt</span>}
                                {skipped && <span className="ml-1.5 text-[10px] font-normal text-slate-500">không phải qua</span>}
                                {/* Badge Cấp-kết-thúc. CHỮ khác KHKK ("⚑ Duyệt thay CEO") có chủ
                                    đích: HĐ không có trạm CEO — kết thúc ở đây nghĩa là HĐ PHÁT
                                    HÀNH luôn + sinh mã (`ContractWorkflowService.cs:527-534`).
                                    `title` cắm trên `<span>` KHÔNG disabled ⇒ tooltip bật thật
                                    (bài S182), và chữ "⚑ Cấp kết thúc" đã nhìn-thấy-được nên
                                    thông tin phân biệt không nằm sau hover. */}
                                {finalizeHere && (
                                  <span
                                    className="ml-1.5 inline-flex items-center rounded bg-violet-100 px-1.5 py-0.5 text-[9px] font-semibold text-violet-800"
                                    title="Cấp này được uỷ quyền KẾT THÚC quy trình: duyệt ở đây là HĐ phát hành luôn, không trình tiếp Bước/Cấp sau. Người duyệt vẫn bỏ tích được để trình tiếp như thường."
                                  >
                                    ⚑ Cấp kết thúc
                                  </span>
                                )}
                              </div>
                              {/* Liệt-kê NV của cấp: người ĐÃ KÝ in đậm, NV cùng cấp còn lại mờ. */}
                              <div className="flex flex-wrap items-center gap-x-1 gap-y-0.5 text-slate-500">
                                {group.length === 0
                                  ? '(chưa cấu hình)'
                                  : group.map((l, i) => {
                                    const sign = signedByLevelId.get(l.id)
                                    return (
                                      <span key={l.id} className="inline-flex items-center gap-1">
                                        {i > 0 && <span className="text-slate-300">/</span>}
                                        <span className={cn(sign ? 'font-semibold text-slate-800' : 'text-slate-400')}>
                                          {l.approverFullName ?? '(chưa cấu hình)'}
                                        </span>
                                        {sign && (
                                          <span className="text-[10px] text-emerald-600">✓ {formatDateTime(sign.signedAt)}</span>
                                        )}
                                      </span>
                                    )
                                  })}
                              </div>
                            </div>
                          </div>
                        </li>
                      )
                    })}
                  </ul>
                )}
              </li>
            )
          })}
        </ol>
      ) : (
        <div className="rounded border border-slate-200 bg-slate-50 px-3 py-2 text-[11px] text-slate-600">
          Quy trình duyệt của loại HĐ này chưa cấu hình Bước/Cấp — liên hệ Admin dựng quy trình
          trước khi trình duyệt.
        </div>
      )}

      {/* ── 3. Banner "đến lượt ai" ─────────────────────────────────────────── */}
      {isWaiting && currentStep && (
        <div className={cn(
          'rounded border px-3 py-2 text-[11px]',
          actorInLevel ? 'border-emerald-200 bg-emerald-50 text-emerald-800' : 'border-amber-300 bg-amber-50 text-amber-800',
        )}>
          <div className="font-medium">
            Đang chờ Bước {currentStep.order} ({currentStep.name}) — Cấp {curLevelOrder}
          </div>
          <div className="mt-0.5">
            Người duyệt: {currentLevels.map(l => l.approverFullName ?? '(chưa rõ)').join(' / ') || '(chưa có)'}
          </div>
          {actorInLevel
            ? <div className="mt-0.5 font-medium">✓ Đến lượt bạn duyệt</div>
            : <div className="mt-0.5">⚠ Không phải lượt bạn — chỉ người trên mới thao tác cấp này</div>}
        </div>
      )}

      {/* HĐ đang chờ duyệt mà cặp con-trỏ trỏ ra ngoài cây (quy trình bị sửa sau khi trình,
          hoặc dữ liệu cũ) — nói bằng chữ thay vì im lặng không hiện banner nào. */}
      {isWaiting && steps.length > 0 && !currentStep && (
        <div className="rounded border border-amber-300 bg-amber-50 px-3 py-2 text-[11px] text-amber-800">
          ⚠ Không xác định được cấp đang chờ (con trỏ Bước {String(curStepIdx ?? '—')} nằm ngoài
          quy trình đã pin). Liên hệ Admin kiểm tra lại quy trình của loại HĐ này.
        </div>
      )}

      {c.phase === ContractPhase.TraLai && (
        <div className="rounded border border-orange-300 bg-orange-50 px-3 py-2 text-[11px] text-orange-800">
          ⚠ HĐ bị TRẢ LẠI — sửa nội dung rồi gửi duyệt lại, quy trình chạy lại từ Bước 1 · Cấp 1.
        </div>
      )}
      {c.phase === ContractPhase.TuChoi && (
        <div className="rounded border border-red-300 bg-red-50 px-3 py-2 text-[11px] text-red-800">
          ✗ HĐ đã bị TỪ CHỐI — không thao tác được nữa.
        </div>
      )}

      {/* ── 4. Hành động — ĐÚNG 2 NÚT (owner @S188 (84)) ─────────────────────── */}
      {/* 🔴 "Từ chối" KHÔNG có đường bấm ở đây, giống KHKK sau khi anh trim S174. Máy BE
          (`ApprovalDecision.Reject` + `targetPhase=TuChoi` ⇒ phase `TuChoi=99`) GIỮ NGUYÊN:
          HĐ cũ đã từ-chối vẫn hiển thị đúng, và mở lại sau này chỉ là thêm nút. */}
      {isWaiting && (
        <div>
          <Label className="text-xs">Hành động:</Label>
          <div className="mt-1 flex flex-wrap gap-1.5">
            <span title={blockedByLevel ? blockedHint : undefined}>
              <button
                type="button"
                disabled={blockedByLevel}
                onClick={() => { if (!blockedByLevel) { setComment(''); setAction('approve') } }}
                className={cn(
                  'rounded border px-2 py-1 text-[11px] font-bold transition',
                  blockedByLevel ? 'cursor-not-allowed border-slate-200 bg-slate-50 text-slate-400' : 'border-emerald-300 text-emerald-700 hover:bg-emerald-50',
                )}
              >
                ✓ Duyệt
              </button>
            </span>
            <span title={blockedByLevel ? blockedHint : undefined}>
              <button
                type="button"
                disabled={blockedByLevel}
                onClick={() => { if (!blockedByLevel) { setComment(''); setAction('return') } }}
                className={cn(
                  'rounded border px-2 py-1 text-[11px] font-bold transition',
                  blockedByLevel ? 'cursor-not-allowed border-slate-200 bg-slate-50 text-slate-400' : 'border-amber-300 text-amber-700 hover:bg-amber-50',
                )}
              >
                ← Trả lại
              </button>
            </span>
          </div>
        </div>
      )}

      {action !== null && (
        <Dialog
          open
          onClose={() => setAction(null)}
          title={action === 'approve' ? '✓ Duyệt hợp đồng' : '← Trả lại người soạn sửa'}
          footer={<>
            <Button variant="outline" onClick={() => setAction(null)}>Hủy</Button>
            <Button onClick={() => transition.mutate(action)} disabled={confirmDisabled}>
              {transition.isPending ? 'Đang gửi...' : 'Xác nhận'}
            </Button>
          </>}
        >
          {action === 'return' && (
            <div className="mb-3 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-[11px] text-amber-800">
              HĐ về trạng thái &ldquo;Trả lại&rdquo;. Người soạn sửa xong gửi lại thì quy trình chạy
              từ Bước 1 · Cấp 1.
            </div>
          )}
          {/* [WQ3b S190 · YC-030 Q3] Ô-tích "Cấp này KẾT THÚC quy trình" — khuôn
              `KhkkWorkflowPanel.tsx:665-679`. CHỈ hiện khi Cấp BE-sẽ-chấm bật cờ
              (`approverFinalizeEligible`), không hiện theo "nhóm có ai đó bật". Hậu quả HĐ nặng
              hơn KHKK nên nói thẳng: kết thúc ở đây là PHÁT HÀNH + sinh mã HĐ, không lùi được. */}
          {action === 'approve' && approverFinalizeEligible && (
            <div className="mb-3 rounded border border-emerald-300 bg-emerald-50 px-3 py-2 text-[12px] text-emerald-800">
              <label className="flex cursor-pointer items-start gap-2">
                <input
                  type="checkbox"
                  className="mt-0.5"
                  checked={applyLevelFinalize}
                  onChange={e => setApplyLevelFinalize(e.target.checked)}
                />
                <span>
                  <span className="font-medium">Cấp này KẾT THÚC quy trình</span>
                  <span className="mt-0.5 block text-[11px] text-emerald-700/80">
                    {applyLevelFinalize
                      ? 'Đã tích — duyệt xong HĐ chuyển thẳng "Đã phát hành" (sinh mã HĐ), KẾT THÚC tại cấp này, KHÔNG trình tiếp cấp sau.'
                      : 'Mặc định: duyệt xong HĐ TRÌNH TIẾP lên cấp sau của quy trình. TÍCH vào nếu muốn kết thúc ngay tại cấp này.'}
                  </span>
                </span>
              </label>
            </div>
          )}
          <Label>
            Ghi chú{commentRequired ? <span className="text-red-500"> *</span> : ' (tùy chọn)'}
          </Label>
          <Textarea
            value={comment}
            onChange={e => setComment(e.target.value)}
            rows={3}
            // Trần 1000 ký tự = ĐÚNG luật BE (`TransitionContractCommandValidator`
            // `ContractFeatures.cs:212` `MaximumLength(1000)`) — KHÔNG chép 2000 của KHKK sang,
            // gõ quá là 400 ở lúc bấm Xác nhận.
            maxLength={1000}
            placeholder={commentRequired ? 'Nêu lý do trả lại...' : 'Ghi chú của bạn (ghi vào phiếu)...'}
          />
          {action === 'approve' && (
            <p className="mt-1 text-[11px] text-slate-500">
              Ghi chú sẽ được ghi vào mục &ldquo;Ý kiến cấp duyệt (Quy trình V2)&rdquo; của HĐ
              (1 ô/Cấp — duyệt lại thì ghi đè ô cũ). Bỏ trống thì hệ thống ghi
              &ldquo;(duyệt — không ý kiến)&rdquo;.
            </p>
          )}
        </Dialog>
      )}
    </div>
  )
}
