"use client";

import { useState } from "react";
import Link from "next/link";
import { clsx } from "clsx";
import { Button } from "@capakraken/ui";
import { Badge } from "@capakraken/ui";
import { trpc } from "~/lib/trpc/client.js";
import { AnimatedModal } from "~/components/ui/AnimatedModal.js";
import { ConfirmDialog } from "~/components/ui/ConfirmDialog.js";
import { ShimmerSkeleton } from "~/components/ui/ShimmerSkeleton.js";

/* ------------------------------------------------------------------ */
/*  Shared types (mirrors API)                                        */
/* ------------------------------------------------------------------ */

type BatchStatus =
  | "DRAFT"
  | "STAGING"
  | "STAGED"
  | "REVIEW_READY"
  | "APPROVED"
  | "COMMITTING"
  | "COMMITTED"
  | "FAILED"
  | "CANCELLED";

type RecordStatus = "PARSED" | "NORMALIZED" | "UNRESOLVED" | "APPROVED" | "REJECTED" | "COMMITTED" | "FAILED";
type UnresolvedAction = "APPROVE" | "REJECT" | "SKIP";

const STATUS_BADGE: Record<
  BatchStatus,
  { label: string; variant: "default" | "success" | "warning" | "danger" | "info" }
> = {
  DRAFT: { label: "Draft", variant: "default" },
  STAGING: { label: "Staging", variant: "info" },
  STAGED: { label: "Staged", variant: "info" },
  REVIEW_READY: { label: "Review Ready", variant: "warning" },
  APPROVED: { label: "Approved", variant: "success" },
  COMMITTING: { label: "Committing", variant: "info" },
  COMMITTED: { label: "Committed", variant: "success" },
  FAILED: { label: "Failed", variant: "danger" },
  CANCELLED: { label: "Cancelled", variant: "default" },
};

const RECORD_STATUS_COLORS: Record<RecordStatus, string> = {
  PARSED: "bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400",
  NORMALIZED: "bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-400",
  UNRESOLVED: "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400",
  APPROVED: "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-400",
  REJECTED: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-400",
  COMMITTED: "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-400",
  FAILED: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-400",
};

type TabKey = "summary" | "resources" | "projects" | "assignments" | "vacations" | "unresolved";

const TABS: { key: TabKey; label: string }[] = [
  { key: "summary", label: "Summary" },
  { key: "resources", label: "Resources" },
  { key: "projects", label: "Projects" },
  { key: "assignments", label: "Assignments" },
  { key: "vacations", label: "Vacations" },
  { key: "unresolved", label: "Unresolved" },
];

/* ------------------------------------------------------------------ */
/*  Helper                                                             */
/* ------------------------------------------------------------------ */

function RecordBadge({ status }: { status: RecordStatus }) {
  return (
    <span
      className={clsx(
        "inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium",
        RECORD_STATUS_COLORS[status] ?? RECORD_STATUS_COLORS.PARSED,
      )}
    >
      {status}
    </span>
  );
}

function formatDate(d: string | null | undefined) {
  if (!d) return "-";
  return new Date(d).toLocaleDateString("de-DE", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

function SummaryCard({
  label,
  count,
  variant = "default",
}: {
  label: string;
  count: number;
  variant?: "default" | "warning" | "danger";
}) {
  return (
    <div
      className={clsx(
        "rounded-xl border p-4",
        variant === "danger"
          ? "border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-900/20"
          : variant === "warning"
            ? "border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-900/20"
            : "border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800",
      )}
    >
      <p className="text-sm text-gray-500 dark:text-gray-400">{label}</p>
      <p
        className={clsx(
          "text-2xl font-bold mt-1",
          variant === "danger"
            ? "text-red-700 dark:text-red-400"
            : variant === "warning"
              ? "text-amber-700 dark:text-amber-400"
              : "text-gray-900 dark:text-gray-100",
        )}
      >
        {count}
      </p>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/*  Tab: Summary                                                       */
/* ------------------------------------------------------------------ */

function SummaryTab({
  batch,
  validationResult,
}: {
  batch: {
    counts: {
      resourceCount: number;
      clientCount: number;
      projectCount: number;
      assignmentCount: number;
      vacationCount: number;
      availabilityRuleCount: number;
      unresolvedCount: number;
    } | null;
    status: BatchStatus;
  };
  validationResult: { blocking: string[]; warnings: string[] } | null;
}) {
  const counts = batch.counts;

  return (
    <div className="space-y-6">
      {/* Counts grid */}
      <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4">
        <SummaryCard label="Resources" count={counts?.resourceCount ?? 0} />
        <SummaryCard label="Projects" count={counts?.projectCount ?? 0} />
        <SummaryCard label="Assignments" count={counts?.assignmentCount ?? 0} />
        <SummaryCard label="Vacations" count={counts?.vacationCount ?? 0} />
        <SummaryCard
          label="Unresolved"
          count={counts?.unresolvedCount ?? 0}
          variant={
            (counts?.unresolvedCount ?? 0) > 0 ? "warning" : "default"
          }
        />
      </div>

      {/* Validation results */}
      {validationResult && (
        <div className="space-y-4">
          {validationResult.blocking.length > 0 && (
            <div className="rounded-xl border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-900/20 p-4">
              <h3 className="text-sm font-semibold text-red-700 dark:text-red-400 mb-2">
                Blocking Issues ({validationResult.blocking.length})
              </h3>
              <ul className="list-disc list-inside text-sm text-red-600 dark:text-red-400 space-y-1">
                {validationResult.blocking.map((msg, i) => (
                  <li key={i}>{msg}</li>
                ))}
              </ul>
            </div>
          )}

          {validationResult.warnings.length > 0 && (
            <div className="rounded-xl border border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-900/20 p-4">
              <h3 className="text-sm font-semibold text-amber-700 dark:text-amber-400 mb-2">
                Warnings ({validationResult.warnings.length})
              </h3>
              <ul className="list-disc list-inside text-sm text-amber-600 dark:text-amber-400 space-y-1">
                {validationResult.warnings.map((msg, i) => (
                  <li key={i}>{msg}</li>
                ))}
              </ul>
            </div>
          )}

          {validationResult.blocking.length === 0 &&
            validationResult.warnings.length === 0 && (
              <div className="rounded-xl border border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-900/20 p-4">
                <p className="text-sm font-medium text-green-700 dark:text-green-400">
                  All validation checks passed. Ready to commit.
                </p>
              </div>
            )}
        </div>
      )}

      {/* Status indicator for failed batches */}
      {batch.status === "FAILED" && (
        <div className="rounded-xl border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-900/20 p-4">
          <h3 className="text-sm font-semibold text-red-700 dark:text-red-400 mb-1">
            Error
          </h3>
          <p className="text-sm text-red-600 dark:text-red-400 font-mono whitespace-pre-wrap">
            This import batch has failed. Check the unresolved records tab for details.
          </p>
        </div>
      )}
    </div>
  );
}

/* ------------------------------------------------------------------ */
/*  Tab: Resources                                                     */
/* ------------------------------------------------------------------ */

function ResourcesTab({ batchId }: { batchId: string }) {
  const [page, setPage] = useState(0);
  const PAGE_SIZE = 50;

  const { data: rawData, isLoading } = trpc.dispo.listStagedResources.useQuery(
    { importBatchId: batchId, limit: PAGE_SIZE },
    { staleTime: 15_000 },
  );
  const data = rawData as { items: Record<string, unknown>[]; nextCursor?: string } | undefined;

  if (isLoading) return <TableSkeleton rows={8} cols={5} />;

  const records = data?.items ?? [];
  const total = records.length;

  return (
    <div>
      <PaginatedTable
        total={total}
        page={page}
        pageSize={PAGE_SIZE}
        onPageChange={setPage}
      >
        <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
          <thead className="bg-gray-50 dark:bg-gray-800/60">
            <tr>
              <Th>Name</Th>
              <Th>EID</Th>
              <Th>Chapter</Th>
              <Th>Status</Th>
              <Th>Warnings</Th>
            </tr>
          </thead>
          <tbody className="divide-y divide-gray-100 dark:divide-gray-700/50">
            {records.map((r: Record<string, unknown>) => (
              <tr key={r.id as string} className="hover:bg-gray-50 dark:hover:bg-gray-700/30">
                <Td>{(r.displayName as string) ?? (r.name as string) ?? "-"}</Td>
                <Td mono>{(r.eid as string) ?? "-"}</Td>
                <Td>{(r.chapter as string) ?? "-"}</Td>
                <Td>
                  <RecordBadge status={(r.status as RecordStatus) ?? "PARSED"} />
                </Td>
                <Td>
                  {Array.isArray(r.warnings) && r.warnings.length > 0 ? (
                    <span className="text-amber-600 dark:text-amber-400 text-xs">
                      {r.warnings.length} warning{r.warnings.length !== 1 ? "s" : ""}
                    </span>
                  ) : (
                    <span className="text-gray-400 text-xs">-</span>
                  )}
                </Td>
              </tr>
            ))}
            {records.length === 0 && (
              <tr>
                <td colSpan={5} className="px-4 py-8 text-center text-sm text-gray-400">
                  No staged resources.
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </PaginatedTable>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/*  Tab: Projects                                                      */
/* ------------------------------------------------------------------ */

function ProjectsTab({ batchId }: { batchId: string }) {
  const [page, setPage] = useState(0);
  const PAGE_SIZE = 50;

  const { data: rawData, isLoading } = trpc.dispo.listStagedProjects.useQuery(
    { importBatchId: batchId, limit: PAGE_SIZE } as { importBatchId: string; limit: number },
    { staleTime: 15_000 },
  );
  const data = rawData as { items: Record<string, unknown>[]; nextCursor?: string } | undefined;

  if (isLoading) return <TableSkeleton rows={8} cols={6} />;

  const records = data?.items ?? [];
  const total = records.length;

  return (
    <PaginatedTable total={total} page={page} pageSize={PAGE_SIZE} onPageChange={setPage}>
      <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
        <thead className="bg-gray-50 dark:bg-gray-800/60">
          <tr>
            <Th>Project Key</Th>
            <Th>Name</Th>
            <Th>Client</Th>
            <Th>TBD</Th>
            <Th>Status</Th>
            <Th>Warnings</Th>
          </tr>
        </thead>
        <tbody className="divide-y divide-gray-100 dark:divide-gray-700/50">
          {records.map((r) => (
            <tr key={r.id as string} className="hover:bg-gray-50 dark:hover:bg-gray-700/30">
              <Td mono>{(r.projectKey as string) ?? "-"}</Td>
              <Td>{(r.name as string) ?? "-"}</Td>
              <Td>{(r.clientName as string) ?? "-"}</Td>
              <Td>
                {r.isTbd ? (
                  <Badge variant="warning">TBD</Badge>
                ) : null}
              </Td>
              <Td>
                <RecordBadge status={(r.status as RecordStatus) ?? "PARSED"} />
              </Td>
              <Td>
                {Array.isArray(r.warnings) && r.warnings.length > 0 ? (
                  <span className="text-amber-600 dark:text-amber-400 text-xs">
                    {r.warnings.length}
                  </span>
                ) : (
                  <span className="text-gray-400 text-xs">-</span>
                )}
              </Td>
            </tr>
          ))}
          {records.length === 0 && (
            <tr>
              <td colSpan={6} className="px-4 py-8 text-center text-sm text-gray-400">
                No staged projects.
              </td>
            </tr>
          )}
        </tbody>
      </table>
    </PaginatedTable>
  );
}

/* ------------------------------------------------------------------ */
/*  Tab: Assignments                                                   */
/* ------------------------------------------------------------------ */

function AssignmentsTab({ batchId }: { batchId: string }) {
  const [page, setPage] = useState(0);
  const PAGE_SIZE = 50;

  const { data: rawData, isLoading } = trpc.dispo.listStagedAssignments.useQuery(
    { importBatchId: batchId, limit: PAGE_SIZE },
    { staleTime: 15_000 },
  );
  const data = rawData as { items: Record<string, unknown>[]; nextCursor?: string } | undefined;

  if (isLoading) return <TableSkeleton rows={8} cols={6} />;

  const records = data?.items ?? [];
  const total = records.length;

  return (
    <PaginatedTable total={total} page={page} pageSize={PAGE_SIZE} onPageChange={setPage}>
      <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
        <thead className="bg-gray-50 dark:bg-gray-800/60">
          <tr>
            <Th>Resource</Th>
            <Th>Project</Th>
            <Th>Date</Th>
            <Th>Hours</Th>
            <Th>Role</Th>
            <Th>Status</Th>
          </tr>
        </thead>
        <tbody className="divide-y divide-gray-100 dark:divide-gray-700/50">
          {records.map((r: Record<string, unknown>, idx: number) => (
            <tr key={(r.id as string) ?? idx} className="hover:bg-gray-50 dark:hover:bg-gray-700/30">
              <Td>{(r.resourceName as string) ?? (r.resourceEid as string) ?? "-"}</Td>
              <Td>{(r.projectName as string) ?? (r.projectKey as string) ?? "-"}</Td>
              <Td mono>{(r.date as string) ?? "-"}</Td>
              <Td>{r.hours != null ? String(r.hours) : "-"}</Td>
              <Td>{(r.roleName as string) ?? "-"}</Td>
              <Td>
                <RecordBadge status={(r.status as RecordStatus) ?? "PARSED"} />
              </Td>
            </tr>
          ))}
          {records.length === 0 && (
            <tr>
              <td colSpan={6} className="px-4 py-8 text-center text-sm text-gray-400">
                No staged assignments.
              </td>
            </tr>
          )}
        </tbody>
      </table>
    </PaginatedTable>
  );
}

/* ------------------------------------------------------------------ */
/*  Tab: Vacations                                                     */
/* ------------------------------------------------------------------ */

function VacationsTab({ batchId }: { batchId: string }) {
  const [page, setPage] = useState(0);
  const PAGE_SIZE = 50;

  const { data: rawData, isLoading } = trpc.dispo.listStagedVacations.useQuery(
    { importBatchId: batchId, limit: PAGE_SIZE },
    { staleTime: 15_000 },
  );
  const data = rawData as { items: Record<string, unknown>[]; nextCursor?: string } | undefined;

  if (isLoading) return <TableSkeleton rows={8} cols={5} />;

  const records = data?.items ?? [];
  const total = records.length;

  return (
    <PaginatedTable total={total} page={page} pageSize={PAGE_SIZE} onPageChange={setPage}>
      <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
        <thead className="bg-gray-50 dark:bg-gray-800/60">
          <tr>
            <Th>Resource</Th>
            <Th>Start</Th>
            <Th>End</Th>
            <Th>Type</Th>
            <Th>Status</Th>
          </tr>
        </thead>
        <tbody className="divide-y divide-gray-100 dark:divide-gray-700/50">
          {records.map((r: Record<string, unknown>, idx: number) => (
            <tr key={(r.id as string) ?? idx} className="hover:bg-gray-50 dark:hover:bg-gray-700/30">
              <Td>{(r.resourceName as string) ?? (r.resourceEid as string) ?? "-"}</Td>
              <Td mono>{(r.startDate as string) ?? "-"}</Td>
              <Td mono>{(r.endDate as string) ?? "-"}</Td>
              <Td>{(r.vacationType as string) ?? "-"}</Td>
              <Td>
                <RecordBadge status={(r.status as RecordStatus) ?? "PARSED"} />
              </Td>
            </tr>
          ))}
          {records.length === 0 && (
            <tr>
              <td colSpan={5} className="px-4 py-8 text-center text-sm text-gray-400">
                No staged vacations.
              </td>
            </tr>
          )}
        </tbody>
      </table>
    </PaginatedTable>
  );
}

/* ------------------------------------------------------------------ */
/*  Tab: Unresolved Review Queue                                       */
/* ------------------------------------------------------------------ */

function UnresolvedTab({ batchId }: { batchId: string }) {
  const utils = trpc.useUtils();

  const { data: rawData, isLoading, refetch } = trpc.dispo.listStagedUnresolvedRecords.useQuery(
    { importBatchId: batchId },
    { staleTime: 10_000 },
  );
  const data = rawData as { items: Record<string, unknown>[]; nextCursor?: string } | undefined;

  const resolveMutation = trpc.dispo.resolveStagedRecord.useMutation({
    onSuccess: () => {
      refetch();
      utils.dispo.getImportBatch.invalidate({ id: batchId });
    },
  });

  const records = data?.items ?? [];
  const remaining = records.filter(
    (r) => r.status === "PARSED" || r.status === "NORMALIZED" || r.status === "UNRESOLVED",
  ).length;

  function handleAction(recordId: string, action: UnresolvedAction) {
    resolveMutation.mutate({
      id: recordId,
      recordType: "UNRESOLVED" as const,
      action,
    });
  }

  function handleBulkApproveNonBlocking() {
    const nonBlocking = records.filter(
      (r: Record<string, unknown>) =>
        r.status === "PARSED" || r.status === "NORMALIZED" || r.status === "UNRESOLVED",
    );
    for (const r of nonBlocking) {
      handleAction(r.id as string, "APPROVE");
    }
  }

  function handleBulkSkipTbd() {
    const tbdRecords = records.filter(
      (r: Record<string, unknown>) =>
        (r.status === "PARSED" || r.status === "NORMALIZED" || r.status === "UNRESOLVED") &&
        typeof r.message === "string" &&
        (r.message as string).toLowerCase().includes("[tbd]"),
    );
    for (const r of tbdRecords) {
      handleAction(r.id as string, "SKIP");
    }
  }

  if (isLoading) return <TableSkeleton rows={6} cols={5} />;

  return (
    <div className="space-y-4">
      {/* Header with bulk actions */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-3">
          <h3 className="text-sm font-semibold text-gray-700 dark:text-gray-300">
            Unresolved Records
          </h3>
          {remaining > 0 && (
            <span className="inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 rounded-full bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400 text-xs font-bold">
              {remaining}
            </span>
          )}
        </div>
        <div className="flex gap-2">
          <Button
            variant="secondary"
            size="sm"
            onClick={handleBulkSkipTbd}
            disabled={resolveMutation.isPending}
          >
            Skip All [tbd]
          </Button>
          <Button
            variant="secondary"
            size="sm"
            onClick={handleBulkApproveNonBlocking}
            disabled={resolveMutation.isPending}
          >
            Approve All Non-Blocking
          </Button>
        </div>
      </div>

      {/* Table */}
      <div className="overflow-x-auto rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
        <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
          <thead className="bg-gray-50 dark:bg-gray-800/60">
            <tr>
              <Th>Type</Th>
              <Th>Message</Th>
              <Th>Source</Th>
              <Th>Hint</Th>
              <Th>Actions</Th>
            </tr>
          </thead>
          <tbody className="divide-y divide-gray-100 dark:divide-gray-700/50">
            {records.map((r: Record<string, unknown>) => {
              const resolved = r.status === "APPROVED" || r.status === "REJECTED";
              return (
                <tr
                  key={r.id as string}
                  className={clsx(
                    "transition-colors",
                    resolved
                      ? "opacity-50 bg-gray-50 dark:bg-gray-800/30"
                      : "hover:bg-gray-50 dark:hover:bg-gray-700/30",
                  )}
                >
                  <Td>
                    <Badge
                      variant="warning"
                    >
                      {(r.recordType as string) ?? "unknown"}
                    </Badge>
                  </Td>
                  <Td>
                    <span className="text-sm text-gray-700 dark:text-gray-300">
                      {(r.message as string) ?? "-"}
                    </span>
                  </Td>
                  <Td>
                    <span className="text-xs text-gray-500 dark:text-gray-400 font-mono">
                      {(r.source as string) ?? "-"}
                    </span>
                  </Td>
                  <Td>
                    <span className="text-xs text-gray-500 dark:text-gray-400 italic">
                      {(r.hint as string) ?? "-"}
                    </span>
                  </Td>
                  <Td>
                    {resolved ? (
                      <span className="text-xs text-gray-400 italic">
                        {r.status as string}
                      </span>
                    ) : (
                      <div className="flex gap-1.5">
                        <button
                          type="button"
                          onClick={() => handleAction(r.id as string, "APPROVE")}
                          disabled={resolveMutation.isPending}
                          className="px-2 py-1 text-xs font-medium rounded-md bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/40 dark:text-green-400 dark:hover:bg-green-900/60 transition-colors disabled:opacity-50"
                        >
                          Approve
                        </button>
                        <button
                          type="button"
                          onClick={() => handleAction(r.id as string, "REJECT")}
                          disabled={resolveMutation.isPending}
                          className="px-2 py-1 text-xs font-medium rounded-md bg-red-100 text-red-600 hover:bg-red-200 dark:bg-red-900/40 dark:text-red-400 dark:hover:bg-red-900/60 transition-colors disabled:opacity-50"
                        >
                          Reject
                        </button>
                        <button
                          type="button"
                          onClick={() => handleAction(r.id as string, "SKIP")}
                          disabled={resolveMutation.isPending}
                          className="px-2 py-1 text-xs font-medium rounded-md bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600 transition-colors disabled:opacity-50"
                        >
                          Skip
                        </button>
                      </div>
                    )}
                  </Td>
                </tr>
              );
            })}
            {records.length === 0 && (
              <tr>
                <td colSpan={5} className="px-4 py-8 text-center text-sm text-gray-400">
                  No unresolved records. Ready to commit.
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </div>

    </div>
  );
}

/* ------------------------------------------------------------------ */
/*  Commit Confirmation Modal                                          */
/* ------------------------------------------------------------------ */

function CommitModal({
  open,
  onClose,
  batchId,
  counts,
  validationResult,
  onCommitted,
}: {
  open: boolean;
  onClose: () => void;
  batchId: string;
  counts: {
    resourceCount: number;
    clientCount: number;
    projectCount: number;
    assignmentCount: number;
    vacationCount: number;
    availabilityRuleCount: number;
    unresolvedCount: number;
  } | null;
  validationResult: { blocking: string[]; warnings: string[] } | null;
  onCommitted: () => void;
}) {
  const commitMutation = trpc.dispo.commitImportBatch.useMutation({
    onSuccess: () => {
      onCommitted();
      onClose();
    },
  });

  const hasBlockers = (validationResult?.blocking.length ?? 0) > 0;

  return (
    <AnimatedModal open={open} onClose={onClose} maxWidth="max-w-lg">
      <div className="px-6 py-5">
        <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
          Commit Import
        </h2>

        {/* Validation results */}
        {validationResult && validationResult.blocking.length > 0 && (
          <div className="rounded-lg border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-900/20 p-3 mb-4">
            <p className="text-sm font-medium text-red-700 dark:text-red-400 mb-1">
              Blocking Issues
            </p>
            <ul className="list-disc list-inside text-xs text-red-600 dark:text-red-400 space-y-0.5">
              {validationResult.blocking.map((msg, i) => (
                <li key={i}>{msg}</li>
              ))}
            </ul>
          </div>
        )}

        {validationResult && validationResult.warnings.length > 0 && (
          <div className="rounded-lg border border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-900/20 p-3 mb-4">
            <p className="text-sm font-medium text-amber-700 dark:text-amber-400 mb-1">
              Warnings
            </p>
            <ul className="list-disc list-inside text-xs text-amber-600 dark:text-amber-400 space-y-0.5">
              {validationResult.warnings.map((msg, i) => (
                <li key={i}>{msg}</li>
              ))}
            </ul>
          </div>
        )}

        {/* Entity counts */}
        {counts && (
          <div className="rounded-lg border border-gray-200 dark:border-gray-700 p-3 mb-4">
            <p className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
              Will be committed:
            </p>
            <div className="grid grid-cols-2 gap-2 text-sm">
              <div className="text-gray-600 dark:text-gray-400">
                Resources: <strong className="text-gray-900 dark:text-gray-100">{counts.resourceCount}</strong>
              </div>
              <div className="text-gray-600 dark:text-gray-400">
                Projects: <strong className="text-gray-900 dark:text-gray-100">{counts.projectCount}</strong>
              </div>
              <div className="text-gray-600 dark:text-gray-400">
                Assignments: <strong className="text-gray-900 dark:text-gray-100">{counts.assignmentCount}</strong>
              </div>
              <div className="text-gray-600 dark:text-gray-400">
                Vacations: <strong className="text-gray-900 dark:text-gray-100">{counts.vacationCount}</strong>
              </div>
            </div>
          </div>
        )}

        {/* Progress during commit */}
        {commitMutation.isPending && (
          <div className="mb-4">
            <div className="flex items-center gap-3">
              <div className="h-2 flex-1 rounded-full bg-gray-200 dark:bg-gray-700 overflow-hidden">
                <div className="h-full bg-brand-600 rounded-full animate-pulse w-2/3" />
              </div>
              <span className="text-sm text-gray-500 dark:text-gray-400">Committing...</span>
            </div>
          </div>
        )}

        {commitMutation.error && (
          <p className="text-sm text-red-600 dark:text-red-400 mb-4">
            {commitMutation.error.message}
          </p>
        )}

        <div className="flex justify-end gap-3">
          <Button variant="secondary" onClick={onClose} disabled={commitMutation.isPending}>
            Cancel
          </Button>
          <Button
            variant="danger"
            onClick={() => commitMutation.mutate({ importBatchId: batchId })}
            disabled={hasBlockers || commitMutation.isPending}
          >
            {commitMutation.isPending ? "Committing..." : "Commit Import"}
          </Button>
        </div>
      </div>
    </AnimatedModal>
  );
}

/* ------------------------------------------------------------------ */
/*  Shared Table Primitives                                            */
/* ------------------------------------------------------------------ */

function Th({ children }: { children: React.ReactNode }) {
  return (
    <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
      {children}
    </th>
  );
}

function Td({ children, mono }: { children: React.ReactNode; mono?: boolean }) {
  return (
    <td
      className={clsx(
        "px-4 py-3 text-sm text-gray-700 dark:text-gray-300",
        mono && "font-mono",
      )}
    >
      {children}
    </td>
  );
}

function TableSkeleton({ rows, cols }: { rows: number; cols: number }) {
  return (
    <div className="space-y-2">
      {Array.from({ length: rows }).map((_, i) => (
        <div key={i} className="flex gap-4">
          {Array.from({ length: cols }).map((_, j) => (
            <ShimmerSkeleton key={j} height={20} rounded="md" className="flex-1" />
          ))}
        </div>
      ))}
    </div>
  );
}

function PaginatedTable({
  total,
  page,
  pageSize,
  onPageChange,
  children,
}: {
  total: number;
  page: number;
  pageSize: number;
  onPageChange: (p: number) => void;
  children: React.ReactNode;
}) {
  const totalPages = Math.max(1, Math.ceil(total / pageSize));

  return (
    <div>
      <div className="overflow-x-auto rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
        {children}
      </div>
      {totalPages > 1 && (
        <div className="flex items-center justify-between mt-3 px-1">
          <p className="text-xs text-gray-500 dark:text-gray-400">
            Showing {page * pageSize + 1}-{Math.min((page + 1) * pageSize, total)} of {total}
          </p>
          <div className="flex gap-1">
            <button
              type="button"
              disabled={page === 0}
              onClick={() => onPageChange(page - 1)}
              className="px-2.5 py-1 text-xs rounded-md border border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
            >
              Prev
            </button>
            <span className="px-2.5 py-1 text-xs text-gray-500 dark:text-gray-400">
              {page + 1} / {totalPages}
            </span>
            <button
              type="button"
              disabled={page >= totalPages - 1}
              onClick={() => onPageChange(page + 1)}
              className="px-2.5 py-1 text-xs rounded-md border border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
            >
              Next
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

/* ------------------------------------------------------------------ */
/*  Main Component                                                     */
/* ------------------------------------------------------------------ */

export function DispoImportDetailClient({ batchId }: { batchId: string }) {
  const [activeTab, setActiveTab] = useState<TabKey>("summary");
  const [showCommitModal, setShowCommitModal] = useState(false);
  const [showCancelConfirm, setShowCancelConfirm] = useState(false);
  const [validationResult, setValidationResult] = useState<{
    blocking: string[];
    warnings: string[];
  } | null>(null);

  const utils = trpc.useUtils();

  const { data: batch, isLoading } = trpc.dispo.getImportBatch.useQuery(
    { id: batchId },
    { staleTime: 5_000 },
  );

  // validateImportBatch is a query, so we use a manual trigger pattern
  const [validationInput, setValidationInput] = useState<{
    chargeabilityWorkbookPath: string;
    planningWorkbookPath: string;
    referenceWorkbookPath: string;
    importBatchId: string;
  } | null>(null);

  const { isFetching: isValidating } = trpc.dispo.validateImportBatch.useQuery(
    validationInput!,
    {
      enabled: validationInput !== null,
      retry: false,
      onSuccess: (result: { blocking: string[]; warnings: string[] }) => {
        setValidationResult(result);
        utils.dispo.getImportBatch.invalidate({ id: batchId });
        setValidationInput(null);
      },
      onError: () => {
        setValidationInput(null);
      },
    } as any,
  );

  const cancelMutation = trpc.dispo.cancelImportBatch.useMutation({
    onSuccess: () => {
      setShowCancelConfirm(false);
      utils.dispo.getImportBatch.invalidate({ id: batchId });
    },
  });

  const status = (batch?.status as BatchStatus) ?? "DRAFT";
  const counts = batch?.counts ?? null;

  const canReview = status === "STAGED" || status === "REVIEW_READY";
  const canValidate = canReview;
  const canCommit = canReview && (validationResult ? validationResult.blocking.length === 0 : false);
  const canCancel = status === "DRAFT" || status === "STAGED" || status === "REVIEW_READY";
  const isReadOnly = status === "COMMITTED" || status === "CANCELLED" || status === "FAILED";

  if (isLoading) {
    return (
      <div className="mx-auto max-w-6xl px-4 py-8 sm:px-6 lg:px-8 space-y-6">
        <ShimmerSkeleton height={40} rounded="lg" />
        <div className="grid grid-cols-5 gap-4">
          {Array.from({ length: 5 }).map((_, i) => (
            <ShimmerSkeleton key={i} height={80} rounded="xl" />
          ))}
        </div>
        <ShimmerSkeleton height={300} rounded="xl" />
      </div>
    );
  }

  if (!batch) {
    return (
      <div className="mx-auto max-w-6xl px-4 py-8 sm:px-6 lg:px-8">
        <div className="rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-12 text-center">
          <p className="text-gray-500 dark:text-gray-400">Batch not found.</p>
          <Link
            href="/admin/dispo-imports"
            className="text-sm text-brand-600 hover:text-brand-800 dark:text-brand-400 mt-2 inline-block"
          >
            Back to list
          </Link>
        </div>
      </div>
    );
  }

  return (
    <div className="mx-auto max-w-6xl px-4 py-8 sm:px-6 lg:px-8">
      {/* Back link */}
      <Link
        href="/admin/dispo-imports"
        className="text-sm text-brand-600 hover:text-brand-800 dark:text-brand-400 dark:hover:text-brand-300 mb-4 inline-flex items-center gap-1"
      >
        <svg className="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
        </svg>
        All Imports
      </Link>

      {/* Header */}
      <div className="flex items-start justify-between mb-6 mt-2">
        <div>
          <div className="flex items-center gap-3">
            <h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
              Import Batch
            </h1>
            <Badge variant={STATUS_BADGE[status]?.variant ?? "default"}>
              {STATUS_BADGE[status]?.label ?? status}
            </Badge>
          </div>
          <p className="text-xs text-gray-400 dark:text-gray-500 font-mono mt-1">
            {batchId}
          </p>
          <div className="flex gap-4 text-xs text-gray-500 dark:text-gray-400 mt-2">
            <span>Created: {formatDate(batch.createdAt as string)}</span>
            <span>Updated: {formatDate(batch.updatedAt as string)}</span>
            {batch.committedAt && (
              <span>Committed: {formatDate(batch.committedAt as string)}</span>
            )}
          </div>
        </div>

        {/* Action buttons */}
        <div className="flex gap-2">
          {canValidate && (
            <Button
              variant="secondary"
              size="sm"
              onClick={() => {
                setValidationInput({
                  chargeabilityWorkbookPath: (batch as any)?.chargeabilitySourceFile ?? "",
                  planningWorkbookPath: (batch as any)?.planningSourceFile ?? "",
                  referenceWorkbookPath: (batch as any)?.referenceSourceFile ?? "",
                  importBatchId: batchId,
                });
              }}
              disabled={isValidating}
            >
              {isValidating ? "Validating..." : "Validate"}
            </Button>
          )}
          {canReview && (
            <Button
              size="sm"
              onClick={() => setShowCommitModal(true)}
              disabled={!canCommit && validationResult !== null}
            >
              Commit
            </Button>
          )}
          {canCancel && (
            <Button
              variant="ghost"
              size="sm"
              onClick={() => setShowCancelConfirm(true)}
            >
              Cancel Import
            </Button>
          )}
        </div>
      </div>

      {/* Tabs */}
      <div className="border-b border-gray-200 dark:border-gray-700 mb-6">
        <nav className="flex gap-0 -mb-px">
          {TABS.map((tab) => (
            <button
              key={tab.key}
              type="button"
              onClick={() => setActiveTab(tab.key)}
              className={clsx(
                "px-4 py-2.5 text-sm font-medium border-b-2 transition-colors",
                activeTab === tab.key
                  ? "border-brand-600 text-brand-700 dark:text-brand-400 dark:border-brand-400"
                  : "border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-600",
              )}
            >
              {tab.label}
              {tab.key === "unresolved" && (counts?.unresolvedCount ?? 0) > 0 && (
                <span className="ml-1.5 inline-flex items-center justify-center min-w-[18px] h-4 px-1 rounded-full bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400 text-[10px] font-bold">
                  {counts!.unresolvedCount}
                </span>
              )}
            </button>
          ))}
        </nav>
      </div>

      {/* Tab content */}
      <div>
        {activeTab === "summary" && (
          <SummaryTab batch={{ ...batch, status, counts }} validationResult={validationResult} />
        )}
        {activeTab === "resources" && <ResourcesTab batchId={batchId} />}
        {activeTab === "projects" && <ProjectsTab batchId={batchId} />}
        {activeTab === "assignments" && <AssignmentsTab batchId={batchId} />}
        {activeTab === "vacations" && <VacationsTab batchId={batchId} />}
        {activeTab === "unresolved" && <UnresolvedTab batchId={batchId} />}
      </div>

      {/* Commit modal */}
      <CommitModal
        open={showCommitModal}
        onClose={() => setShowCommitModal(false)}
        batchId={batchId}
        counts={counts}
        validationResult={validationResult}
        onCommitted={() => utils.dispo.getImportBatch.invalidate({ id: batchId })}
      />

      {/* Cancel confirmation */}
      {showCancelConfirm && (
        <ConfirmDialog
          title="Cancel Import"
          message="Are you sure you want to cancel this import batch? All staged records will be discarded."
          confirmLabel={cancelMutation.isPending ? "Cancelling..." : "Cancel Import"}
          variant="danger"
          onConfirm={() => cancelMutation.mutate({ id: batchId })}
          onCancel={() => setShowCancelConfirm(false)}
        />
      )}
    </div>
  );
}
