import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import {
  BarChart,
  Bar,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  ResponsiveContainer,
  AreaChart,
  Area,
} from 'recharts';
import {
  Wrench,
  ClipboardCheck,
  CalendarDays,
  RefreshCw,
  ArrowRight,
  AlertTriangle,
  Clock,
  CheckCircle2,
  TrendingUp,
  ClipboardList,
  Tag,
  User,
  Trash2,
  ArrowUpDown,
  ChevronUp,
  ChevronDown,
  ChevronLeft,
  ChevronRight,
  Search,
  X,
} from 'lucide-react';
import { dataService } from '../services/dataService';
import { IProject, ITicket, IWorkOrder, IPMSchedule, ITicketActivity, TicketStatus, IUser } from '../types';
import { useTheme } from '../App';
import { getTicketProjectIdString, getTicketProjectName } from '../utils/ticketProject';
import { ticketPriorityVi, ticketStatusVi, ticketTypeVi } from '../utils/displayVi';
import Modal from '../components/Modal';
import { useAuthStore } from '../store/useAuthStore';
import { useUIStore } from '../store/useUIStore';
import { useAssetStore } from '../store/useAssetStore';
import { isSystemAdministrator } from '../utils/permissions';
import { resolveUserAssigneeDisplay } from '../utils/omOperationsShared';

function ticketStatusBadgeClass(status: string): string {
  const s = String(status);
  if (s === TicketStatus.OPEN || s === 'Open') {
    return 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300';
  }
  if (s === TicketStatus.ASSIGNED || s === 'Assigned') {
    return 'bg-violet-100 dark:bg-violet-900/30 text-violet-800 dark:text-violet-200';
  }
  if (s === TicketStatus.IN_PROGRESS || s === 'InProgress' || s === 'In Progress') {
    return 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300';
  }
  if (s === TicketStatus.PENDING_PARTS || s === 'PendingParts') {
    return 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300';
  }
  if (s === TicketStatus.RESOLVED || s === 'Resolved') {
    return 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300';
  }
  if (s === TicketStatus.CLOSED || s === 'Closed') {
    return 'bg-slate-200 dark:bg-slate-600 text-slate-800 dark:text-slate-100';
  }
  return 'bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300';
}

function ticketPriorityBadgeClass(priority: string): string {
  const p = String(priority);
  if (p === 'Critical') {
    return 'bg-red-100 dark:bg-red-900/35 text-red-800 dark:text-red-200 border border-red-200/80 dark:border-red-800/50';
  }
  if (p === 'High') {
    return 'bg-orange-100 dark:bg-orange-900/35 text-orange-900 dark:text-orange-200 border border-orange-200/70 dark:border-orange-800/45';
  }
  if (p === 'Medium') {
    return 'bg-amber-100 dark:bg-amber-900/30 text-amber-900 dark:text-amber-200 border border-amber-200/60 dark:border-amber-800/40';
  }
  if (p === 'Low') {
    return 'bg-emerald-100 dark:bg-emerald-900/25 text-emerald-900 dark:text-emerald-200 border border-emerald-200/50 dark:border-emerald-800/35';
  }
  return 'bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-200 border border-slate-200/80 dark:border-slate-600/60';
}

function formatActor(ref: string | { name?: string; email?: string } | undefined): string {
  if (ref == null) return '—';
  if (typeof ref === 'object') {
    return ref.name || ref.email || '—';
  }
  return String(ref);
}

function isTicketSlaOverdue(ticket: ITicket): boolean {
  if (!ticket.slaDeadline) return false;
  const status = String(ticket.status);
  if (status === TicketStatus.RESOLVED || status === TicketStatus.CLOSED || status === 'Resolved' || status === 'Closed') {
    return false;
  }
  const deadlineMs = new Date(ticket.slaDeadline).getTime();
  if (Number.isNaN(deadlineMs)) return false;
  return deadlineMs < Date.now();
}

type TicketManageSortField = 'code' | 'title' | 'project' | 'priority' | 'status' | 'createdAt';

const PRIORITY_RANK: Record<string, number> = {
  Low: 0,
  Medium: 1,
  High: 2,
  Critical: 3,
};

const TICKET_PAGE_SIZES = [10, 20, 50, 100] as const;

function resolveProjectContextId(projList: IProject[], selectedProjectId: string): string {
  if (selectedProjectId && projList.some((p) => String(p._id) === String(selectedProjectId))) {
    return selectedProjectId;
  }
  return projList[0]?._id != null ? String(projList[0]._id) : '';
}

function dedupeById<T extends { _id?: unknown }>(items: T[]): T[] {
  const unique = new Map<string, T>();
  items.forEach((item) => {
    const id = item?._id != null ? String(item._id) : '';
    if (!id) return;
    if (!unique.has(id)) unique.set(id, item);
  });
  return Array.from(unique.values());
}

async function loadAcrossProjects<T extends { _id?: unknown }>(
  projList: IProject[],
  loader: (projectId: string) => Promise<T[]>
): Promise<T[]> {
  const projectIds = projList
    .map((p) => (p?._id != null ? String(p._id) : ''))
    .filter(Boolean);
  if (!projectIds.length) return [];
  const rows = await Promise.all(projectIds.map((projectId) => loader(projectId).catch(() => [] as T[])));
  return dedupeById(rows.flat());
}

const DashboardOM: React.FC = () => {
  const { isDarkMode } = useTheme();
  const { user } = useAuthStore();
  const selectedProjectId = useAssetStore((s) => s.selectedProjectId);
  const { addNotification } = useUIStore();
  const [loading, setLoading] = useState(true);
  const [refreshing, setRefreshing] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [projects, setProjects] = useState<IProject[]>([]);
  const [users, setUsers] = useState<IUser[]>([]);
  const [tickets, setTickets] = useState<ITicket[]>([]);
  const [workOrders, setWorkOrders] = useState<IWorkOrder[]>([]);
  const [pmSchedules, setPmSchedules] = useState<IPMSchedule[]>([]);
  const [lastUpdated, setLastUpdated] = useState<Date>(new Date());
  const [viewingTicket, setViewingTicket] = useState<ITicket | null>(null);
  const [ticketActivities, setTicketActivities] = useState<ITicketActivity[]>([]);
  const [ticketActivitiesLoading, setTicketActivitiesLoading] = useState(false);
  const [ticketDeleting, setTicketDeleting] = useState(false);
  const [ticketSortField, setTicketSortField] = useState<TicketManageSortField>('createdAt');
  const [ticketSortDir, setTicketSortDir] = useState<'asc' | 'desc'>('desc');
  const [ticketPageSize, setTicketPageSize] = useState<number>(10);
  const [ticketPage, setTicketPage] = useState(1);
  const [ticketFilterSearch, setTicketFilterSearch] = useState('');
  const [ticketFilterProjectId, setTicketFilterProjectId] = useState('');
  const [ticketFilterPriority, setTicketFilterPriority] = useState<string>('all');
  const [ticketFilterStatus, setTicketFilterStatus] = useState<string>('all');
  /** yyyy-mm-dd — lọc theo ngày tạo phiếu (createdAt), để trống = không giới hạn */
  const [ticketFilterCreatedFrom, setTicketFilterCreatedFrom] = useState('');
  const [ticketFilterCreatedTo, setTicketFilterCreatedTo] = useState('');

  const canSystemAdminDelete = isSystemAdministrator(user);

  const handleTicketSort = useCallback((field: TicketManageSortField) => {
    setTicketSortField((prev) => {
      if (prev === field) {
        setTicketSortDir((d) => (d === 'asc' ? 'desc' : 'asc'));
        return prev;
      }
      setTicketSortDir(field === 'createdAt' ? 'desc' : 'asc');
      return field;
    });
  }, []);

  const clearTicketFilters = useCallback(() => {
    setTicketFilterSearch('');
    setTicketFilterProjectId('');
    setTicketFilterPriority('all');
    setTicketFilterStatus('all');
    setTicketFilterCreatedFrom('');
    setTicketFilterCreatedTo('');
  }, []);

  const hasActiveTicketFilters = useMemo(
    () =>
      ticketFilterSearch.trim() !== '' ||
      ticketFilterProjectId !== '' ||
      ticketFilterPriority !== 'all' ||
      ticketFilterStatus !== 'all' ||
      ticketFilterCreatedFrom.trim() !== '' ||
      ticketFilterCreatedTo.trim() !== '',
    [
      ticketFilterSearch,
      ticketFilterProjectId,
      ticketFilterPriority,
      ticketFilterStatus,
      ticketFilterCreatedFrom,
      ticketFilterCreatedTo,
    ]
  );

  const closeTicketDetail = useCallback(() => {
    setViewingTicket(null);
    setTicketActivities([]);
    setTicketActivitiesLoading(false);
  }, []);

  const openTicketDetail = useCallback(async (t: ITicket) => {
    setViewingTicket(t);
    setTicketActivities([]);
    setTicketActivitiesLoading(true);
    try {
      const acts = await dataService.getTicketActivities(t._id);
      setTicketActivities(acts);
    } catch {
      setTicketActivities([]);
    } finally {
      setTicketActivitiesLoading(false);
    }
  }, []);

  const load = useCallback(async (isRefresh = false) => {
    try {
      if (isRefresh) setRefreshing(true);
      else setLoading(true);
      setError(null);
      const projList = await dataService.getProjects();
      const usersList = await dataService.getUserDirectory();
      const pid = resolveProjectContextId(projList, selectedProjectId);
      setProjects(projList);
      setUsers(usersList || []);

      const [tix, wos, pms] = await Promise.all([
        dataService.getTickets().catch(() => []),
        loadAcrossProjects(projList, (projectId) => dataService.getWorkOrders(projectId)),
        pid ? dataService.getPMSchedules(pid).catch(() => []) : Promise.resolve<IPMSchedule[]>([]),
      ]);
      setTickets(tix || []);
      setWorkOrders(wos || []);
      setPmSchedules(pms || []);
      setLastUpdated(new Date());
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Không thể tải O&M Dashboard');
    } finally {
      setLoading(false);
      setRefreshing(false);
    }
  }, [selectedProjectId]);

  const handleDeleteViewingTicket = useCallback(async () => {
    if (!canSystemAdminDelete || !viewingTicket) return;
    const t = viewingTicket;
    const label = t.code ? `phiếu ${t.code}` : 'phiếu này';
    if (!window.confirm(`Xóa vĩnh viễn ${label}? Hành động không hoàn tác.`)) return;
    try {
      setTicketDeleting(true);
      await dataService.deleteTicket(t._id);
      addNotification('success', 'Đã xóa phiếu.');
      closeTicketDetail();
      await load(true);
    } catch (err: unknown) {
      addNotification('error', err instanceof Error ? err.message : 'Không xóa được phiếu.');
    } finally {
      setTicketDeleting(false);
    }
  }, [addNotification, canSystemAdminDelete, viewingTicket, closeTicketDetail, load]);

  useEffect(() => {
    load();
  }, [load]);

  const projectNameById = useMemo(() => {
    const m = new Map<string, string>();
    projects.forEach((p) => {
      const id = String(p._id);
      m.set(id, p.name);
    });
    return m;
  }, [projects]);

  const stats = useMemo(() => {
    const isOpen = (t: ITicket) => !['Closed', 'Resolved'].includes(String(t.status));
    const openTickets = tickets.filter(isOpen).length;
    const criticalOpen = tickets.filter(
      (t) => isOpen(t) && String(t.priority) === 'Critical'
    ).length;
    const woOpen = workOrders.filter((w) =>
      ['Draft', 'Approved', 'InProgress', 'In Progress'].includes(String(w.status))
    ).length;
    const woDone = workOrders.filter((w) => ['Completed', 'Verified'].includes(String(w.status))).length;
    const now = new Date();
    const pmActive = pmSchedules.filter((s) => s.status === 'Active').length;
    const pmOverdue = pmSchedules.filter((s) => {
      if (s.status !== 'Active') return false;
      try {
        return new Date(s.nextDue) < now;
      } catch {
        return false;
      }
    }).length;
    const pmUpcoming7 = pmSchedules.filter((s) => {
      if (s.status !== 'Active') return false;
      try {
        const d = new Date(s.nextDue);
        const diff = (d.getTime() - now.getTime()) / (86400000);
        return diff >= 0 && diff <= 7;
      } catch {
        return false;
      }
    }).length;
    return { openTickets, criticalOpen, woOpen, woDone, pmActive, pmOverdue, pmUpcoming7, ticketTotal: tickets.length };
  }, [tickets, workOrders, pmSchedules]);

  const ticketsByProject = useMemo(() => {
    const map = new Map<string, { count: number; label: string }>();
    tickets.forEach((t) => {
      const pid = getTicketProjectIdString(t);
      if (!pid) return;
      const label = getTicketProjectName(t, projectNameById);
      const prev = map.get(pid);
      const bestLabel = label !== '—' ? label : prev?.label && prev.label !== '—' ? prev.label : label;
      map.set(pid, { count: (prev?.count || 0) + 1, label: bestLabel });
    });
    return Array.from(map.entries())
      .map(([id, { count, label }]) => ({
        name: label !== '—' ? label : id.slice(0, 8),
        count,
      }))
      .sort((a, b) => b.count - a.count)
      .slice(0, 8);
  }, [tickets, projectNameById]);

  const ticketTrend = useMemo(() => {
    const byDay = new Map<string, number>();
    const days = 14;
    const now = new Date();
    for (let i = days - 1; i >= 0; i--) {
      const d = new Date(now);
      d.setDate(d.getDate() - i);
      const key = d.toISOString().split('T')[0];
      byDay.set(key, 0);
    }
    tickets.forEach((t) => {
      try {
        const d = new Date(t.createdAt);
        const key = d.toISOString().split('T')[0];
        if (byDay.has(key)) byDay.set(key, (byDay.get(key) || 0) + 1);
      } catch {
        /* skip */
      }
    });
    return Array.from(byDay.entries()).map(([date, created]) => ({
      date: date.slice(5),
      created,
    }));
  }, [tickets]);

  const ticketManageStatusOptions = useMemo(() => {
    const uniq = new Set<string>();
    tickets.forEach((t) => uniq.add(String(t.status)));
    return Array.from(uniq).sort((a, b) => a.localeCompare(b, 'vi', { sensitivity: 'base' }));
  }, [tickets]);

  const projectsSortedForFilter = useMemo(
    () => [...projects].sort((a, b) => String(a.name).localeCompare(String(b.name), 'vi', { sensitivity: 'base' })),
    [projects]
  );

  const manageTicketsFiltered = useMemo(() => {
    const q = ticketFilterSearch.trim().toLowerCase();
    const fromStr = ticketFilterCreatedFrom.trim();
    const toStr = ticketFilterCreatedTo.trim();
    const fromMs = fromStr !== '' ? new Date(`${fromStr}T00:00:00`).getTime() : null;
    const toMs = toStr !== '' ? new Date(`${toStr}T23:59:59.999`).getTime() : null;

    return tickets.filter((t) => {
      if (q) {
        const code = String(t.code || '').toLowerCase();
        const title = String(t.title || '').toLowerCase();
        const proj = getTicketProjectName(t, projectNameById).toLowerCase();
        const st = String(t.status).toLowerCase();
        const pr = String(t.priority).toLowerCase();
        if (
          !code.includes(q) &&
          !title.includes(q) &&
          !proj.includes(q) &&
          !st.includes(q) &&
          !pr.includes(q)
        ) {
          return false;
        }
      }
      if (ticketFilterProjectId && getTicketProjectIdString(t) !== ticketFilterProjectId) {
        return false;
      }
      if (ticketFilterPriority !== 'all' && String(t.priority) !== ticketFilterPriority) {
        return false;
      }
      if (ticketFilterStatus !== 'all' && String(t.status) !== ticketFilterStatus) {
        return false;
      }
      if (fromMs !== null || toMs !== null) {
        const ts = new Date(t.createdAt).getTime();
        if (Number.isNaN(ts)) return false;
        if (fromMs !== null && !Number.isNaN(fromMs) && ts < fromMs) return false;
        if (toMs !== null && !Number.isNaN(toMs) && ts > toMs) return false;
      }
      return true;
    });
  }, [
    tickets,
    ticketFilterSearch,
    ticketFilterProjectId,
    ticketFilterPriority,
    ticketFilterStatus,
    ticketFilterCreatedFrom,
    ticketFilterCreatedTo,
    projectNameById,
  ]);

  const manageTicketsSorted = useMemo(() => {
    const list = [...manageTicketsFiltered];
    const dir = ticketSortDir === 'asc' ? 1 : -1;
    list.sort((a, b) => {
      let cmp = 0;
      switch (ticketSortField) {
        case 'code':
          cmp = String(a.code || '').localeCompare(String(b.code || ''), 'vi', { numeric: true });
          break;
        case 'title':
          cmp = String(a.title || '').localeCompare(String(b.title || ''), 'vi', { sensitivity: 'base' });
          break;
        case 'project':
          cmp = getTicketProjectName(a, projectNameById).localeCompare(
            getTicketProjectName(b, projectNameById),
            'vi',
            { sensitivity: 'base' }
          );
          break;
        case 'priority': {
          const ra = PRIORITY_RANK[String(a.priority)] ?? 99;
          const rb = PRIORITY_RANK[String(b.priority)] ?? 99;
          cmp = ra - rb;
          break;
        }
        case 'status':
          cmp = String(a.status).localeCompare(String(b.status), 'vi', { sensitivity: 'base' });
          break;
        case 'createdAt':
          cmp = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
          break;
        default:
          cmp = 0;
      }
      return cmp * dir;
    });
    return list;
  }, [manageTicketsFiltered, ticketSortField, ticketSortDir, projectNameById]);

  const workOrdersByTicketId = useMemo(() => {
    const map = new Map<string, IWorkOrder[]>();
    workOrders.forEach((wo) => {
      const tid = wo.ticketId != null ? String(wo.ticketId).trim() : '';
      if (!tid) return;
      const prev = map.get(tid) || [];
      prev.push(wo);
      map.set(tid, prev);
    });
    return map;
  }, [workOrders]);

  const ticketListTotal = manageTicketsSorted.length;
  const ticketTotalPages =
    ticketListTotal === 0 ? 1 : Math.max(1, Math.ceil(ticketListTotal / ticketPageSize));
  const ticketPageSafe = Math.min(Math.max(1, ticketPage), ticketTotalPages);
  const ticketPageStart = (ticketPageSafe - 1) * ticketPageSize;
  const manageTicketsPage = manageTicketsSorted.slice(ticketPageStart, ticketPageStart + ticketPageSize);

  useEffect(() => {
    const tp = ticketListTotal === 0 ? 1 : Math.max(1, Math.ceil(ticketListTotal / ticketPageSize));
    setTicketPage((p) => Math.min(p, tp));
  }, [ticketListTotal, ticketPageSize]);

  useEffect(() => {
    setTicketPage(1);
  }, [
    ticketFilterSearch,
    ticketFilterProjectId,
    ticketFilterPriority,
    ticketFilterStatus,
    ticketFilterCreatedFrom,
    ticketFilterCreatedTo,
  ]);

  /** Chọn đúng dự án + mở phiếu trên trang Operations (xử lý query trong Operations.tsx) */
  const operationsDeepLink = useMemo(() => {
    if (!viewingTicket) return '/operations';
    const pid = getTicketProjectIdString(viewingTicket);
    if (!pid) return '/operations';
    return `/operations?project=${encodeURIComponent(pid)}&ticket=${encodeURIComponent(String(viewingTicket._id))}`;
  }, [viewingTicket]);

  const gridText = isDarkMode ? '#94a3b8' : '#64748b';
  const tooltipBg = isDarkMode ? '#0f172a' : '#fff';
  const tooltipBorder = isDarkMode ? '#334155' : '#e2e8f0';

  if (loading && !tickets.length && !workOrders.length) {
    return (
      <div className="flex items-center justify-center h-64 text-slate-500 dark:text-slate-400">
        Đang tải O&amp;M Dashboard...
      </div>
    );
  }

  return (
    <div className="space-y-6 pb-10">
      <header className="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 shadow-sm px-4 py-3">
        <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
          <div className="flex items-start gap-3 min-w-0">
            <div
              className="flex h-8 w-8 sm:h-9 sm:w-9 shrink-0 rounded-lg bg-indigo-100 dark:bg-indigo-500/15 text-indigo-600 dark:text-indigo-400 items-center justify-center border border-indigo-200/80 dark:border-indigo-500/25"
              aria-hidden
            >
              <Wrench size={18} strokeWidth={2} />
            </div>
            <div className="min-w-0">
              <p className="text-[10px] font-semibold uppercase tracking-wide text-indigo-600 dark:text-indigo-400">
                O&amp;M Command Center
              </p>
              <h1 className="text-lg sm:text-xl font-bold text-slate-900 dark:text-white leading-tight mt-0.5">
                O&amp;M Dashboard
              </h1>
              <p className="text-xs text-slate-500 dark:text-slate-400 mt-1 max-w-2xl line-clamp-2">
                Ticket, work order và lịch bảo trì đa dự án trên một màn hình.
              </p>
            </div>
          </div>
          <div className="flex flex-wrap items-center gap-2 sm:justify-end shrink-0">
            <span className="text-[11px] text-slate-500 dark:text-slate-400 tabular-nums">
              Cập nhật: {lastUpdated.toLocaleString('vi-VN')}
            </span>
            <button
              type="button"
              onClick={() => load(true)}
              disabled={refreshing}
              className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium border border-slate-200 dark:border-slate-600 bg-slate-50 dark:bg-slate-700/40 text-slate-700 dark:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-700 disabled:opacity-50 transition-colors"
            >
              <RefreshCw size={14} className={refreshing ? 'animate-spin' : ''} />
              Làm mới
            </button>
          </div>
        </div>
      </header>

      {error && (
        <div className="rounded-lg border border-red-200 dark:border-red-900/50 bg-red-50 dark:bg-red-900/20 px-4 py-3 text-sm text-red-800 dark:text-red-200">
          {error}
        </div>
      )}

      <div className="grid grid-cols-2 xl:grid-cols-4 gap-4">
        <div className="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-4 shadow-sm">
          <div className="flex items-center gap-2 text-amber-600 dark:text-amber-400 text-xs font-bold uppercase">
            <AlertTriangle size={14} />
            Ticket mở
          </div>
          <p className="text-3xl font-bold text-slate-900 dark:text-white mt-2">{stats.openTickets}</p>
          <p className="text-xs text-slate-500 mt-1">/ {stats.ticketTotal} tổng · {stats.criticalOpen} Critical</p>
        </div>
        <div className="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-4 shadow-sm">
          <div className="flex items-center gap-2 text-blue-600 dark:text-blue-400 text-xs font-bold uppercase">
            <ClipboardCheck size={14} />
            Work order
          </div>
          <p className="text-3xl font-bold text-slate-900 dark:text-white mt-2">{stats.woOpen}</p>
          <p className="text-xs text-slate-500 mt-1">{stats.woDone} đã hoàn thành</p>
        </div>
        <div className="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-4 shadow-sm">
          <div className="flex items-center gap-2 text-emerald-600 dark:text-emerald-400 text-xs font-bold uppercase">
            <CalendarDays size={14} />
            PM hoạt động
          </div>
          <p className="text-3xl font-bold text-slate-900 dark:text-white mt-2">{stats.pmActive}</p>
          <p className="text-xs text-slate-500 mt-1">{stats.pmOverdue} quá hạn · {stats.pmUpcoming7} đến hạn 7 ngày</p>
        </div>
        <div className="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-4 shadow-sm">
          <div className="flex items-center gap-2 text-violet-600 dark:text-violet-400 text-xs font-bold uppercase">
            <TrendingUp size={14} />
            Dự án
          </div>
          <p className="text-3xl font-bold text-slate-900 dark:text-white mt-2">{projects.length}</p>
          <p className="text-xs text-slate-500 mt-1">Nguồn dữ liệu đa site</p>
        </div>
      </div>

      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
        <div className="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-4 shadow-sm">
          <h2 className="text-sm font-bold text-slate-800 dark:text-white mb-4">Ticket tạo theo ngày (14 ngày)</h2>
          <div className="h-64 min-h-[256px] w-full min-w-0">
            <ResponsiveContainer width="100%" height="100%" minWidth={0} debounce={100}>
              <AreaChart data={ticketTrend} margin={{ top: 8, right: 8, left: 0, bottom: 0 }}>
                <defs>
                  <linearGradient id="omFill" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor="#6366f1" stopOpacity={0.35} />
                    <stop offset="100%" stopColor="#6366f1" stopOpacity={0} />
                  </linearGradient>
                </defs>
                <CartesianGrid strokeDasharray="3 3" stroke={gridText} opacity={0.25} />
                <XAxis dataKey="date" tick={{ fontSize: 10, fill: gridText }} />
                <YAxis tick={{ fontSize: 11, fill: gridText }} allowDecimals={false} />
                <Tooltip
                  contentStyle={{
                    background: tooltipBg,
                    border: `1px solid ${tooltipBorder}`,
                    borderRadius: 8,
                    color: isDarkMode ? '#e2e8f0' : '#0f172a',
                  }}
                />
                <Area type="monotone" dataKey="created" stroke="#6366f1" fill="url(#omFill)" strokeWidth={2} name="Ticket mới" />
              </AreaChart>
            </ResponsiveContainer>
          </div>
        </div>

        <div className="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-4 shadow-sm">
          <h2 className="text-sm font-bold text-slate-800 dark:text-white mb-4">Khối lượng ticket theo dự án</h2>
          <div className="h-64 min-h-[256px] w-full min-w-0">
            <ResponsiveContainer width="100%" height="100%" minWidth={0} debounce={100}>
              <BarChart data={ticketsByProject} layout="vertical" margin={{ top: 8, right: 16, left: 8, bottom: 8 }}>
                <CartesianGrid strokeDasharray="3 3" stroke={gridText} opacity={0.25} />
                <XAxis type="number" tick={{ fontSize: 11, fill: gridText }} allowDecimals={false} />
                <YAxis type="category" dataKey="name" width={100} tick={{ fontSize: 10, fill: gridText }} />
                <Tooltip
                  contentStyle={{
                    background: tooltipBg,
                    border: `1px solid ${tooltipBorder}`,
                    borderRadius: 8,
                    color: isDarkMode ? '#e2e8f0' : '#0f172a',
                  }}
                />
                <Bar dataKey="count" fill="#3b82f6" radius={[0, 4, 4, 0]} name="Tickets" />
              </BarChart>
            </ResponsiveContainer>
          </div>
        </div>
      </div>

      <div className="rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 overflow-hidden shadow-sm">
        <div className="px-4 py-3 border-b border-slate-200 dark:border-slate-700 flex flex-wrap items-center justify-between gap-2">
          <div>
            <h2 className="text-sm font-bold text-slate-800 dark:text-white">Quản lý ticket</h2>
            <p className="text-xs text-slate-500 dark:text-slate-400 mt-0.5">
              Đa dự án — bấm một dòng để xem chi tiết phiếu
            </p>
          </div>
          <div className="flex flex-wrap gap-2">
            <Link
              to="/operations"
              className="text-xs font-semibold text-blue-600 dark:text-blue-400 hover:underline inline-flex items-center gap-1"
            >
              Phiếu sự cố <ArrowRight size={12} />
            </Link>
            <Link
              to="/work-orders"
              className="text-xs font-semibold text-emerald-600 dark:text-emerald-400 hover:underline inline-flex items-center gap-1"
            >
              Work order <ArrowRight size={12} />
            </Link>
            <Link
              to="/schedule"
              className="text-xs font-semibold text-violet-600 dark:text-violet-400 hover:underline inline-flex items-center gap-1"
            >
              Lịch PM <ArrowRight size={12} />
            </Link>
          </div>
        </div>
        <div className="px-3 py-3 border-b border-slate-200 dark:border-slate-700 bg-slate-50/80 dark:bg-slate-900/40">
          <p className="text-[11px] font-semibold text-slate-600 dark:text-slate-400 mb-2 uppercase tracking-wide">
            Bộ lọc
          </p>
          <div className="flex flex-wrap items-end gap-2 sm:gap-3">
            <label className="flex flex-col gap-1 min-w-[160px] flex-1 sm:max-w-xs">
              <span className="text-xs text-slate-600 dark:text-slate-400">Tìm kiếm</span>
              <span className="relative">
                <Search
                  className="absolute left-2 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none"
                  size={14}
                  aria-hidden
                />
                <input
                  type="search"
                  value={ticketFilterSearch}
                  onChange={(e) => setTicketFilterSearch(e.target.value)}
                  placeholder="Mã, tiêu đề, dự án..."
                  className="w-full rounded-md border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 pl-7 pr-2 py-1.5 text-xs text-slate-800 dark:text-slate-200 placeholder:text-slate-400"
                />
              </span>
            </label>
            <label className="flex flex-col gap-1">
              <span className="text-xs text-slate-600 dark:text-slate-400">Dự án</span>
              <select
                value={ticketFilterProjectId}
                onChange={(e) => setTicketFilterProjectId(e.target.value)}
                className="rounded-md border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 px-2 py-1.5 text-xs font-medium text-slate-800 dark:text-slate-200 max-w-[10rem] sm:max-w-[14rem]"
              >
                <option value="">Tất cả dự án</option>
                {projectsSortedForFilter.map((p) => (
                  <option key={p._id} value={String(p._id)}>
                    {p.name}
                  </option>
                ))}
              </select>
            </label>
            <label className="flex flex-col gap-1">
              <span className="text-xs text-slate-600 dark:text-slate-400">Ưu tiên</span>
              <select
                value={ticketFilterPriority}
                onChange={(e) => setTicketFilterPriority(e.target.value)}
                className="rounded-md border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 px-2 py-1.5 text-xs font-medium text-slate-800 dark:text-slate-200"
              >
                <option value="all">Tất cả</option>
                <option value="Low">{ticketPriorityVi('Low')}</option>
                <option value="Medium">{ticketPriorityVi('Medium')}</option>
                <option value="High">{ticketPriorityVi('High')}</option>
                <option value="Critical">{ticketPriorityVi('Critical')}</option>
              </select>
            </label>
            <label className="flex flex-col gap-1">
              <span className="text-xs text-slate-600 dark:text-slate-400">Trạng thái</span>
              <select
                value={ticketFilterStatus}
                onChange={(e) => setTicketFilterStatus(e.target.value)}
                className="rounded-md border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 px-2 py-1.5 text-xs font-medium text-slate-800 dark:text-slate-200 min-w-[7.5rem] max-w-[12rem]"
              >
                <option value="all">Tất cả</option>
                {ticketManageStatusOptions.map((s) => (
                  <option key={s} value={s}>
                    {ticketStatusVi(s)}
                  </option>
                ))}
              </select>
            </label>
            <label className="flex flex-col gap-1">
              <span className="text-xs text-slate-600 dark:text-slate-400 inline-flex items-center gap-1">
                <CalendarDays size={12} className="text-slate-400 shrink-0" aria-hidden />
                Tạo từ
              </span>
              <input
                type="date"
                value={ticketFilterCreatedFrom}
                onChange={(e) => setTicketFilterCreatedFrom(e.target.value)}
                className="rounded-md border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 px-2 py-1.5 text-xs text-slate-800 dark:text-slate-200 min-w-[10.5rem]"
                aria-label="Lọc phiếu tạo từ ngày"
              />
            </label>
            <label className="flex flex-col gap-1">
              <span className="text-xs text-slate-600 dark:text-slate-400 inline-flex items-center gap-1">
                <CalendarDays size={12} className="text-slate-400 shrink-0" aria-hidden />
                Đến ngày
              </span>
              <input
                type="date"
                value={ticketFilterCreatedTo}
                onChange={(e) => setTicketFilterCreatedTo(e.target.value)}
                className="rounded-md border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 px-2 py-1.5 text-xs text-slate-800 dark:text-slate-200 min-w-[10.5rem]"
                aria-label="Lọc phiếu đến ngày tạo"
              />
            </label>
            <button
              type="button"
              onClick={clearTicketFilters}
              disabled={!hasActiveTicketFilters}
              className="inline-flex items-center gap-1 rounded-md border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 px-2.5 py-1.5 text-xs font-semibold text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800 disabled:opacity-40 disabled:pointer-events-none"
              title={hasActiveTicketFilters ? 'Xóa tất cả bộ lọc' : 'Không có bộ lọc đang áp dụng'}
            >
              <X size={14} aria-hidden />
              Xóa lọc
            </button>
          </div>
        </div>
        <div className="px-3 py-2 border-b border-slate-200 dark:border-slate-700 flex flex-wrap items-center justify-between gap-3 gap-y-2 text-xs">
          <p className="text-slate-600 dark:text-slate-400">
            {ticketListTotal === 0
              ? '0 phiếu'
              : `Hiển thị ${ticketPageStart + 1}–${Math.min(ticketPageStart + ticketPageSize, ticketListTotal)} trong ${ticketListTotal} phiếu`}
          </p>
          <div className="flex flex-wrap items-center gap-2 sm:gap-3">
            <label className="flex items-center gap-1.5 text-slate-600 dark:text-slate-400">
              <span className="whitespace-nowrap">Hàng / trang</span>
              <select
                value={ticketPageSize}
                onChange={(e) => {
                  setTicketPageSize(Number(e.target.value));
                  setTicketPage(1);
                }}
                className="rounded-md border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 px-2 py-1 text-slate-800 dark:text-slate-200 text-xs font-medium"
              >
                {TICKET_PAGE_SIZES.map((n) => (
                  <option key={n} value={n}>
                    {n}
                  </option>
                ))}
              </select>
            </label>
            <div className="flex items-center gap-1 rounded-md border border-slate-200 dark:border-slate-600 p-0.5 bg-slate-50 dark:bg-slate-900/50">
              <button
                type="button"
                onClick={() => setTicketPage((p) => Math.max(1, p - 1))}
                disabled={ticketPageSafe <= 1}
                className="p-1 rounded disabled:opacity-40 disabled:pointer-events-none hover:bg-white dark:hover:bg-slate-800 text-slate-700 dark:text-slate-300"
                aria-label="Trang trước"
              >
                <ChevronLeft size={18} />
              </button>
              <span className="px-2 tabular-nums text-slate-700 dark:text-slate-300 min-w-[5.75rem] text-center">
                {ticketPageSafe} / {ticketTotalPages}
              </span>
              <button
                type="button"
                onClick={() => setTicketPage((p) => Math.min(ticketTotalPages, p + 1))}
                disabled={ticketPageSafe >= ticketTotalPages}
                className="p-1 rounded disabled:opacity-40 disabled:pointer-events-none hover:bg-white dark:hover:bg-slate-800 text-slate-700 dark:text-slate-300"
                aria-label="Trang sau"
              >
                <ChevronRight size={18} />
              </button>
            </div>
          </div>
        </div>
        <div className="overflow-x-auto">
          <table className="min-w-full text-sm">
            <thead className="bg-slate-50 dark:bg-slate-900/50 text-slate-500 dark:text-slate-400">
              <tr>
                {(
                  [
                    ['code', 'Mã'],
                    ['title', 'Tiêu đề'],
                    ['project', 'Dự án'],
                    ['priority', 'Ưu tiên'],
                    ['status', 'Trạng thái'],
                    ['createdAt', 'Thời gian'],
                  ] as const
                ).map(([field, label]) => (
                  <th key={field} className="text-left px-4 py-2 font-semibold">
                    <button
                      type="button"
                      onClick={() => handleTicketSort(field)}
                      className={`inline-flex items-center gap-1.5 w-full text-left rounded-md -mx-1 px-1 py-0.5 hover:bg-slate-100 dark:hover:bg-slate-800 hover:text-slate-900 dark:hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${
                        ticketSortField === field ? 'text-slate-900 dark:text-white' : ''
                      }`}
                      aria-sort={
                        ticketSortField === field
                          ? ticketSortDir === 'asc'
                            ? 'ascending'
                            : 'descending'
                          : 'none'
                      }
                    >
                      <span>{label}</span>
                      {ticketSortField === field ? (
                        ticketSortDir === 'asc' ? (
                          <ChevronUp size={14} className="shrink-0 text-blue-600 dark:text-blue-400" aria-hidden />
                        ) : (
                          <ChevronDown size={14} className="shrink-0 text-blue-600 dark:text-blue-400" aria-hidden />
                        )
                      ) : (
                        <ArrowUpDown size={13} className="shrink-0 opacity-35" aria-hidden />
                      )}
                    </button>
                  </th>
                ))}
                <th className="text-left px-4 py-2 font-semibold whitespace-nowrap">Được giao</th>
                <th className="text-left px-4 py-2 font-semibold whitespace-nowrap">Công việc (Work Order)</th>
                <th className="text-left px-4 py-2 font-semibold whitespace-nowrap">Hạn SLA</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-slate-100 dark:divide-slate-700">
              {ticketListTotal === 0 ? (
                <tr>
                  <td colSpan={9} className="px-4 py-10 text-center text-slate-500 dark:text-slate-400">
                    {tickets.length === 0
                      ? 'Chưa có ticket.'
                      : 'Không có phiếu khớp bộ lọc. Thử đổi từ khóa hoặc bấm Xóa lọc.'}
                  </td>
                </tr>
              ) : (
                manageTicketsPage.map((t) => (
                  <tr
                    key={t._id}
                    role="button"
                    tabIndex={0}
                    onClick={() => openTicketDetail(t)}
                    onKeyDown={(e) => {
                      if (e.key === 'Enter' || e.key === ' ') {
                        e.preventDefault();
                        openTicketDetail(t);
                      }
                    }}
                    className="cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-700/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500/60"
                  >
                    <td className="px-4 py-2 font-mono text-xs text-slate-600 dark:text-slate-300">{t.code}</td>
                    <td className="px-4 py-2 text-slate-800 dark:text-slate-200 max-w-[200px] truncate" title={t.title}>
                      {t.title}
                    </td>
                    <td className="px-4 py-2 text-slate-600 dark:text-slate-400">
                      {getTicketProjectName(t, projectNameById)}
                    </td>
                    <td className="px-4 py-2">
                      <span
                        className={`inline-flex text-xs font-semibold px-2 py-0.5 rounded-full whitespace-nowrap ${ticketPriorityBadgeClass(String(t.priority))}`}
                      >
                        {ticketPriorityVi(String(t.priority))}
                      </span>
                    </td>
                    <td className="px-4 py-2">
                      <span
                        className={`inline-flex text-xs font-semibold px-2 py-0.5 rounded-full whitespace-nowrap ${ticketStatusBadgeClass(String(t.status))}`}
                      >
                        {ticketStatusVi(String(t.status))}
                      </span>
                    </td>
                    <td className="px-4 py-2 text-slate-500 text-xs whitespace-nowrap">
                      <span className="inline-flex items-center gap-1">
                        <Clock size={12} />
                        {new Date(t.createdAt).toLocaleString('vi-VN')}
                      </span>
                    </td>
                    <td className="px-4 py-2 text-slate-600 dark:text-slate-300 text-xs whitespace-nowrap">
                      {resolveUserAssigneeDisplay(t.assignedTo as unknown as string | { name?: string; email?: string }, users, 'Chưa gán')}
                    </td>
                    <td className="px-4 py-2 text-xs">
                      <div className="flex flex-wrap gap-1.5 min-w-[14rem]">
                        {(workOrdersByTicketId.get(String(t._id)) || []).length === 0 ? (
                          <span className="text-slate-400 dark:text-slate-500">—</span>
                        ) : (
                          (workOrdersByTicketId.get(String(t._id)) || []).map((wo) => {
                            const progress = Math.max(
                              0,
                              Math.min(
                                100,
                                Number.isFinite(Number(wo.checklistProgress))
                                  ? Number(wo.checklistProgress)
                                  : (wo.status === 'Completed' || wo.status === 'Verified' ? 100 : 0)
                              )
                            );
                            const badgeStyle =
                              progress <= 0
                                ? { background: '#ef4444' } // Chưa thực hiện: đỏ 100%
                                : progress >= 100
                                  ? { background: '#16a34a' } // Hoàn tất: xanh lá 100%
                                  : progress >= 50
                                    ? {
                                        // 50-99%: phần tiến độ vàng, phần còn lại xanh
                                        background: `linear-gradient(90deg, #facc15 0%, #facc15 ${progress}%, #22c55e ${progress}%, #22c55e 100%)`,
                                      }
                                    : {
                                        // 1-49%: phần tiến độ cam, phần còn lại xanh
                                        background: `linear-gradient(90deg, #fb923c 0%, #fb923c ${progress}%, #22c55e ${progress}%, #22c55e 100%)`,
                                      };
                            return (
                              <span
                                key={wo._id}
                                className="inline-flex items-center rounded-full border border-slate-200/70 dark:border-slate-600/70 text-white px-2.5 py-0.5 whitespace-nowrap shadow-sm"
                                style={badgeStyle}
                                title={`${wo.woCode || 'WO'} - Tiến độ ${progress}%`}
                              >
                                <span className="font-semibold tracking-wide">{wo.woCode || 'WO'}</span>
                              </span>
                            );
                          })
                        )}
                      </div>
                    </td>
                    <td className="px-4 py-2 text-xs whitespace-nowrap">
                      <span
                        className={`inline-flex items-center gap-1 ${
                          isTicketSlaOverdue(t)
                            ? 'animate-pulse text-red-600 dark:text-red-400 font-semibold'
                            : 'text-slate-500'
                        }`}
                      >
                        <Clock size={12} />
                        {t.slaDeadline ? new Date(t.slaDeadline).toLocaleString('vi-VN') : '—'}
                      </span>
                    </td>
                  </tr>
                ))
              )}
            </tbody>
          </table>
        </div>
      </div>

      <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
        <Link
          to="/operations"
          className="group rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-5 shadow-sm hover:border-blue-300 dark:hover:border-blue-700 transition-colors"
        >
          <ClipboardCheck className="text-blue-500 mb-3" size={24} />
          <h3 className="font-bold text-slate-900 dark:text-white">Trang Operations</h3>
          <p className="text-xs text-slate-500 mt-1">Tạo, phân công, đóng phiếu theo dự án.</p>
          <span className="inline-flex items-center gap-1 text-sm font-semibold text-blue-600 mt-3 group-hover:gap-2 transition-all">
            Mở Operations <ArrowRight size={14} />
          </span>
        </Link>
        <Link
          to="/work-orders"
          className="group rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-5 shadow-sm hover:border-emerald-300 dark:hover:border-emerald-700 transition-colors"
        >
          <CheckCircle2 className="text-emerald-500 mb-3" size={24} />
          <h3 className="font-bold text-slate-900 dark:text-white">Work orders</h3>
          <p className="text-xs text-slate-500 mt-1">Theo dõi WO đa dự án, trạng thái thực địa.</p>
          <span className="inline-flex items-center gap-1 text-sm font-semibold text-emerald-600 mt-3 group-hover:gap-2 transition-all">
            Mở Work orders <ArrowRight size={14} />
          </span>
        </Link>
        <Link
          to="/schedule"
          className="group rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 p-5 shadow-sm hover:border-violet-300 dark:hover:border-violet-700 transition-colors"
        >
          <CalendarDays className="text-violet-500 mb-3" size={24} />
          <h3 className="font-bold text-slate-900 dark:text-white">Lịch bảo trì</h3>
          <p className="text-xs text-slate-500 mt-1">PM schedule, quá hạn và kỳ tới.</p>
          <span className="inline-flex items-center gap-1 text-sm font-semibold text-violet-600 mt-3 group-hover:gap-2 transition-all">
            Mở Lịch PM <ArrowRight size={14} />
          </span>
        </Link>
      </div>

      <Modal
        isOpen={!!viewingTicket}
        onClose={closeTicketDetail}
        title="Chi tiết phiếu sự cố"
        maxWidth="2xl"
        footer={
          <div className="flex flex-col gap-3 w-full">
            <div className="flex flex-col-reverse sm:flex-row gap-2 w-full sm:justify-end">
              <button
                type="button"
                onClick={closeTicketDetail}
                className="w-full sm:w-auto inline-flex justify-center rounded-lg border border-slate-300 dark:border-slate-600 px-4 py-2 text-sm font-medium text-slate-700 dark:text-slate-200 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600"
              >
                Đóng
              </button>
              <Link
                to={operationsDeepLink}
                onClick={closeTicketDetail}
                className="w-full sm:w-auto inline-flex justify-center items-center gap-1.5 rounded-lg border border-transparent px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700"
              >
                Mở Operations <ArrowRight size={14} />
              </Link>
            </div>
            {canSystemAdminDelete && viewingTicket ? (
              <div className="pt-2 border-t border-slate-200 dark:border-slate-600 flex justify-start">
                <button
                  type="button"
                  onClick={() => void handleDeleteViewingTicket()}
                  disabled={ticketDeleting}
                  className="inline-flex items-center justify-center gap-2 rounded-lg border border-red-200 dark:border-red-900/50 px-4 py-2 text-sm font-medium text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-950/30 hover:bg-red-100 dark:hover:bg-red-950/50 disabled:opacity-50 disabled:pointer-events-none w-full sm:w-auto"
                >
                  <Trash2 size={16} />
                  {ticketDeleting ? 'Đang xóa…' : 'Xóa phiếu (Admin hệ thống)'}
                </button>
              </div>
            ) : null}
          </div>
        }
      >
        {viewingTicket && (
          <div className="space-y-5 max-h-[min(70vh,640px)] overflow-y-auto pr-1 -mr-1">
            <div className="border-b border-slate-200 dark:border-slate-700 pb-3">
              <h3 className="text-lg font-bold text-slate-900 dark:text-white flex flex-wrap items-center gap-2">
                {viewingTicket.title}
                <span
                  className={`text-xs px-2 py-0.5 rounded-full font-semibold ${ticketStatusBadgeClass(String(viewingTicket.status))}`}
                >
                  {ticketStatusVi(String(viewingTicket.status))}
                </span>
              </h3>
              <p className="text-sm text-slate-500 dark:text-slate-400 mt-1">
                Mã: <span className="font-mono font-medium text-slate-700 dark:text-slate-300">{viewingTicket.code}</span>
                {' · '}
                Dự án:{' '}
                <span className="font-medium text-slate-700 dark:text-slate-300">
                  {getTicketProjectName(viewingTicket, projectNameById)}
                </span>
              </p>
            </div>

            {viewingTicket.description ? (
              <div className="rounded-lg border border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-900/40 p-3">
                <h4 className="text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400 mb-1">
                  Mô tả
                </h4>
                <p className="text-sm text-slate-700 dark:text-slate-200 whitespace-pre-line">{viewingTicket.description}</p>
              </div>
            ) : null}

            <div className="rounded-lg border border-slate-200 dark:border-slate-700 p-3 grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm">
              <div className="flex items-start gap-2 text-slate-600 dark:text-slate-400">
                <Tag size={16} className="text-blue-500 shrink-0 mt-0.5" />
                <span>
                  Loại: <b className="text-slate-800 dark:text-slate-200">{ticketTypeVi(viewingTicket.ticketType)}</b>
                </span>
              </div>
              <div className="flex items-start gap-2 text-slate-600 dark:text-slate-400">
                <AlertTriangle
                  size={16}
                  className={
                    viewingTicket.priority === 'High' || viewingTicket.priority === 'Critical'
                      ? 'text-red-500 shrink-0 mt-0.5'
                      : 'text-amber-500 shrink-0 mt-0.5'
                  }
                />
                <span>
                  Ưu tiên: <b className="text-slate-800 dark:text-slate-200">{ticketPriorityVi(String(viewingTicket.priority))}</b>
                </span>
              </div>
              <div className="flex items-start gap-2 text-slate-600 dark:text-slate-400">
                <User size={16} className="text-violet-500 shrink-0 mt-0.5" />
                <span>
                  Được giao:{' '}
                  <b className="text-slate-800 dark:text-slate-200">
                    {formatActor(viewingTicket.assignedTo as unknown as { name?: string })}
                  </b>
                </span>
              </div>
              <div className="flex items-start gap-2 text-slate-600 dark:text-slate-400">
                <Clock size={16} className="text-red-500 shrink-0 mt-0.5" />
                <span>
                  Hạn SLA:{' '}
                  <b className="text-slate-800 dark:text-slate-200">
                    {viewingTicket.slaDeadline
                      ? new Date(viewingTicket.slaDeadline).toLocaleString('vi-VN')
                      : '—'}
                  </b>
                </span>
              </div>
              <div className="flex items-start gap-2 text-slate-600 dark:text-slate-400 sm:col-span-2">
                <Clock size={16} className="text-slate-400 shrink-0 mt-0.5" />
                <span>
                  Tạo lúc:{' '}
                  <b className="text-slate-800 dark:text-slate-200">
                    {new Date(viewingTicket.createdAt).toLocaleString('vi-VN')}
                  </b>
                </span>
              </div>
            </div>

            <div>
              <h4 className="text-sm font-bold text-slate-700 dark:text-slate-300 mb-2 flex items-center gap-2">
                <ClipboardList size={16} /> Lộ trình phiếu
              </h4>
              {ticketActivitiesLoading ? (
                <p className="text-sm text-slate-500 dark:text-slate-400 italic py-2">Đang tải lộ trình…</p>
              ) : ticketActivities.length === 0 ? (
                <p className="text-sm text-slate-500 dark:text-slate-400 py-2">Chưa có hoạt động ghi nhận.</p>
              ) : (
                <ul className="space-y-2 border border-slate-200 dark:border-slate-700 rounded-lg divide-y divide-slate-100 dark:divide-slate-700 overflow-hidden">
                  {[...ticketActivities]
                    .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
                    .map((act) => (
                      <li key={act._id} className="px-3 py-2.5 bg-white dark:bg-slate-800/50">
                        <div className="flex justify-between gap-2 flex-wrap">
                          <span className="text-sm font-semibold text-slate-800 dark:text-white">{act.action}</span>
                          <span className="text-xs text-slate-500 whitespace-nowrap">
                            {new Date(act.timestamp).toLocaleString('vi-VN')}
                          </span>
                        </div>
                        {act.statusBefore && act.statusAfter && act.statusBefore !== act.statusAfter ? (
                          <p className="text-xs text-slate-500 mt-1">
                            <span className={ticketStatusBadgeClass(String(act.statusBefore)) + ' px-1.5 py-0.5 rounded'}>
                              {ticketStatusVi(String(act.statusBefore))}
                            </span>
                            <span className="mx-1">→</span>
                            <span className={ticketStatusBadgeClass(String(act.statusAfter)) + ' px-1.5 py-0.5 rounded'}>
                              {ticketStatusVi(String(act.statusAfter))}
                            </span>
                          </p>
                        ) : null}
                        <p className="text-xs text-slate-500 dark:text-slate-400 mt-1">
                          Bởi: {formatActor(act.performedBy as unknown as { name?: string })}
                        </p>
                        {act.details ? (
                          <p className="text-xs text-slate-600 dark:text-slate-300 mt-2 p-2 rounded bg-slate-50 dark:bg-slate-900/50 border border-slate-100 dark:border-slate-700">
                            {act.details}
                          </p>
                        ) : null}
                      </li>
                    ))}
                </ul>
              )}
            </div>
          </div>
        )}
      </Modal>
    </div>
  );
};

export default DashboardOM;
