import * as React from "react";
import {
  AlertCircleIcon,
  CheckCircle2Icon,
  CircleAlertIcon,
  FileTextIcon,
  Trash2Icon,
} from "lucide-react";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogFooter,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
import { Skeleton } from "@/components/ui/skeleton";
import { Spinner } from "@/components/ui/spinner";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export type FilePreviewState =
  | "loading"
  | "preview"
  | "empty"
  | "error"
  | "importing";

export type CsvRowStatus = "pending" | "success" | "failed";

export interface CsvPreviewColumn {
  key: string;
  /** Display label shown in the column header. */
  label: string;
}

export interface CsvPreviewRow extends Record<string, string | undefined> {
  _status?: CsvRowStatus;
  /** Shown in a tooltip when status is "failed". */
  _statusMessage?: string;
}

export interface StaffOption {
  id: string;
  name: string;
}

export interface FilePreviewDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  /** Name of the file being previewed. */
  fileName?: string;
  state?: FilePreviewState;
  errorMessage?: string;
  /** Ordered list of columns to display. */
  columns?: CsvPreviewColumn[];
  /** Data rows — keys match CsvPreviewColumn.key. */
  rows?: CsvPreviewRow[];
  /** Called when the user edits a cell inline. */
  onRowChange?: (rowIndex: number, key: string, value: string) => void;
  /** Called when the user clicks the delete icon on a row. */
  onRowDelete?: (rowIndex: number) => void;
  /** Called when the user clicks "Import". */
  onImport?: () => void;
  /** Called when the user clicks "Cancel" during import. */
  onCancelImport?: () => void;
  /** 0–100 progress value shown in the importing state. */
  importProgress?: number;
  /** Total rows in the file (shown in the preview header). */
  totalRows?: number;
  /** Rows that passed validation (shown in the preview header). */
  validRows?: number;
  /** Rows per page for the preview table. Defaults to 10. */
  pageSize?: number;
  /**
   * List of staff members available for assignment.
   * When provided, a basic staff selector is rendered above the table.
   * Import is blocked until a staff member is selected.
   * Ignored when `staffSelector` is provided.
   */
  staffOptions?: StaffOption[];
  /** Currently selected staff ID. */
  selectedStaffId?: string;
  /** Called when the user picks a staff member. */
  onStaffSelect?: (staffId: string) => void;
  /**
   * Custom staff-selector block rendered above the table. When provided, replaces
   * the entire built-in "Assign staff" block (label + select + helper text).
   * The caller still drives `selectedStaffId` so the Import button gating works.
   */
  staffSelector?: React.ReactNode;
  /**
   * Rendered below the staff selector while previewing. Its purpose is the batch
   * tag step: tags chosen here apply to every client in the import, so it belongs
   * beside the assignment rather than after it.
   */
  tagStep?: React.ReactNode;
  className?: string;
}

// ---------------------------------------------------------------------------
// Sub-components
// ---------------------------------------------------------------------------

function LoadingState({ columnCount }: { columnCount: number }) {
  const cols = Math.max(columnCount, 3);
  return (
    <Table>
      <TableHeader>
        <TableRow>
          {Array.from({ length: cols }).map((_, i) => (
            <TableHead key={i}>
              <Skeleton className="h-4 w-24" />
            </TableHead>
          ))}
        </TableRow>
      </TableHeader>
      <TableBody>
        {Array.from({ length: 4 }).map((_, rowIdx) => (
          <TableRow key={rowIdx}>
            {Array.from({ length: cols }).map((_, colIdx) => (
              <TableCell key={colIdx}>
                <Skeleton className="h-4 w-full" />
              </TableCell>
            ))}
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}

function EmptyState() {
  return (
    <div className="flex flex-col items-center gap-3 py-12 text-center">
      <FileTextIcon className="size-10 text-muted-foreground/40" />
      <p className="text-sm font-medium text-foreground">No data found</p>
      <p className="text-xs text-muted-foreground">
        The CSV file appears to be empty or contains only headers.
      </p>
    </div>
  );
}

function ErrorState({ message }: { message?: string }) {
  return (
    <div className="flex flex-col items-center gap-3 py-12 text-center">
      <AlertCircleIcon className="size-10 text-destructive/60" />
      <p className="text-sm font-medium text-foreground">
        Unable to parse file
      </p>
      <p className="text-xs text-muted-foreground">
        {message ??
          "The file could not be read. Please check the format and try again."}
      </p>
    </div>
  );
}

function ImportingState({
  progress,
  successCount,
  failedCount,
}: {
  progress: number;
  successCount: number;
  failedCount: number;
}) {
  return (
    <div className="flex flex-col items-center gap-4 py-8">
      <Spinner className="size-6" />
      <div className="w-full max-w-sm space-y-2">
        <Progress value={progress} className="h-1.5" />
        <div className="flex items-center justify-between text-xs text-muted-foreground">
          <span>Importing… {progress}%</span>
          {(successCount > 0 || failedCount > 0) && (
            <span>
              {successCount} success · {failedCount} failed
            </span>
          )}
        </div>
      </div>
    </div>
  );
}

function RowStatusCell({ row }: { row: CsvPreviewRow }) {
  const { _status, _statusMessage } = row;

  if (_status === "success") {
    return (
      <div className="flex items-center justify-center h-full px-2">
        <CheckCircle2Icon className="size-4 text-success" />
      </div>
    );
  }

  if (_status === "failed") {
    return (
      <TooltipProvider>
        <Tooltip>
          <TooltipTrigger
            render={
              <div className="flex items-center justify-center h-full px-2 cursor-default">
                <CircleAlertIcon className="size-4 text-destructive" />
              </div>
            }
          />
          {_statusMessage && (
            <TooltipContent side="top">{_statusMessage}</TooltipContent>
          )}
        </Tooltip>
      </TooltipProvider>
    );
  }

  return null;
}

// ---------------------------------------------------------------------------
// Main component
// ---------------------------------------------------------------------------

export function FilePreviewDialog({
  open,
  onOpenChange,
  fileName,
  state = "preview",
  errorMessage,
  columns = [],
  rows = [],
  onRowChange,
  onRowDelete,
  onImport,
  onCancelImport,
  importProgress = 0,
  totalRows,
  validRows,
  pageSize = 10,
  staffOptions,
  selectedStaffId,
  onStaffSelect,
  staffSelector,
  tagStep,
  className,
}: FilePreviewDialogProps) {
  const [page, setPage] = React.useState(0);

  // Reset page when rows change
  React.useEffect(() => {
    setPage(0);
  }, [rows.length]);

  const totalPages = Math.ceil(rows.length / pageSize);
  const pagedRows = rows.slice(page * pageSize, (page + 1) * pageSize);
  const pageStart = page * pageSize; // used for row index display

  const isImporting = state === "importing";
  const hasBuiltInStaffSelector = !!staffOptions && staffOptions.length > 0;
  const hasStaffSelector = !!staffSelector || hasBuiltInStaffSelector;
  // Import is blocked when staff selection is required but none is chosen yet
  const canImport =
    state === "preview" &&
    rows.length > 0 &&
    (!hasStaffSelector || !!selectedStaffId);

  const successCount = rows.filter((r) => r._status === "success").length;
  const failedCount = rows.filter((r) => r._status === "failed").length;
  const hasStatus = rows.some((r) => r._status !== undefined);

  return (
    <Dialog open={open} onOpenChange={isImporting ? undefined : onOpenChange}>
      <DialogContent size="3xl" className={className}>
        <DialogHeader>
          <DialogTitle>Preview Import</DialogTitle>
        </DialogHeader>

        {/* File name + row counts */}
        {fileName && state !== "error" && (
          <div className="flex items-center justify-between text-xs text-muted-foreground">
            <span className="font-medium text-foreground">{fileName}</span>
            {state === "preview" && totalRows !== undefined && (
              <span>
                {validRows ?? rows.length} valid row
                {(validRows ?? rows.length) !== 1 ? "s" : ""} / {totalRows}{" "}
                total
              </span>
            )}
            {hasStatus && (
              <span>
                {successCount} success · {failedCount} failed
              </span>
            )}
          </div>
        )}

        {/* Content by state */}
        {state === "loading" && <LoadingState columnCount={columns.length} />}
        {state === "empty" && <EmptyState />}
        {state === "error" && <ErrorState message={errorMessage} />}
        {state === "importing" && (
          <ImportingState
            progress={importProgress}
            successCount={successCount}
            failedCount={failedCount}
          />
        )}

        {state === "preview" && (
          <>
            {/* Staff assignment — custom slot wins; otherwise fall back to built-in select */}
            {staffSelector
              ? staffSelector
              : hasBuiltInStaffSelector && (
                  <div className="flex flex-col gap-1">
                    <label className="text-label-medium text-foreground">
                      Assign staff{" "}
                      <span className="text-destructive" aria-hidden="true">
                        *
                      </span>
                    </label>
                    <Select
                      value={selectedStaffId ?? ""}
                      onValueChange={(id) => onStaffSelect?.(id ?? "")}
                    >
                      <SelectTrigger className="w-full">
                        <SelectValue placeholder="Select a staff member" />
                      </SelectTrigger>
                      <SelectContent>
                        {staffOptions!.map((s) => (
                          <SelectItem key={s.id} value={s.id}>
                            {s.name}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <p className="text-xs text-muted-foreground">
                      All clients in this import will be assigned to the
                      selected staff member.
                    </p>
                  </div>
                )}

            {/* Batch tags — after the owner, since the reading is "assign, then label". */}
            {tagStep}

            <div className="max-h-[360px] overflow-auto border border-border">
              <Table>
                <TableHeader className="sticky top-0 z-10 bg-muted/80 backdrop-blur-sm">
                  <TableRow>
                    {/* Index column */}
                    <TableHead className="w-10 text-center select-none">
                      #
                    </TableHead>
                    {columns.map((col) => (
                      <TableHead key={col.key} className="select-none">
                        {col.label}
                      </TableHead>
                    ))}
                    {/* Status column — only shown when any row has status */}
                    {hasStatus && (
                      <TableHead className="w-16 text-center select-none">
                        Status
                      </TableHead>
                    )}
                    {/* Delete column */}
                    {onRowDelete && <TableHead className="w-10 select-none" />}
                  </TableRow>
                </TableHeader>

                <TableBody>
                  {pagedRows.map((row, pageRowIdx) => {
                    const absoluteIdx = pageStart + pageRowIdx;
                    return (
                      <TableRow
                        key={absoluteIdx}
                        className={cn(
                          row._status === "failed" && "bg-destructive/5",
                          row._status === "success" && "bg-success/5",
                        )}
                      >
                        {/* Index cell */}
                        <TableCell className="text-center text-xs text-muted-foreground select-none w-10">
                          {absoluteIdx + 1}
                        </TableCell>

                        {/* Data cells */}
                        {columns.map((col) => (
                          <TableCell key={col.key} className="p-0">
                            <input
                              type="text"
                              value={row[col.key] ?? ""}
                              onChange={(e) =>
                                onRowChange?.(
                                  absoluteIdx,
                                  col.key,
                                  e.target.value,
                                )
                              }
                              className={cn(
                                "w-full bg-transparent px-4 py-3 text-sm text-foreground",
                                "focus:outline-none focus:ring-1 focus:ring-inset focus:ring-primary",
                              )}
                            />
                          </TableCell>
                        ))}

                        {/* Status cell */}
                        {hasStatus && (
                          <TableCell className="text-center w-16 p-0">
                            <RowStatusCell row={row} />
                          </TableCell>
                        )}

                        {/* Delete cell */}
                        {onRowDelete && (
                          <TableCell className="w-10 p-0 text-center">
                            <Button
                              variant="ghost"
                              size="icon-sm"
                              aria-label={`Delete row ${absoluteIdx + 1}`}
                              onClick={() => onRowDelete(absoluteIdx)}
                              className="text-muted-foreground hover:text-destructive"
                            >
                              <Trash2Icon className="size-3.5" />
                            </Button>
                          </TableCell>
                        )}
                      </TableRow>
                    );
                  })}
                </TableBody>
              </Table>
            </div>

            {/* Pagination */}
            {totalPages > 1 && (
              <div className="flex items-center justify-between text-xs text-muted-foreground pt-1">
                <span>
                  Showing {pageStart + 1}–
                  {Math.min(pageStart + pageSize, rows.length)} of {rows.length}
                </span>
                <div className="flex items-center gap-1">
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={() => setPage((p) => Math.max(0, p - 1))}
                    disabled={page === 0}
                  >
                    Previous
                  </Button>
                  <span className="px-2">
                    {page + 1} / {totalPages}
                  </span>
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={() =>
                      setPage((p) => Math.min(totalPages - 1, p + 1))
                    }
                    disabled={page >= totalPages - 1}
                  >
                    Next
                  </Button>
                </div>
              </div>
            )}
          </>
        )}

        <DialogFooter>
          {isImporting ? (
            <Button variant="outline" onClick={onCancelImport}>
              Cancel Import
            </Button>
          ) : (
            <>
              <Button variant="outline" onClick={() => onOpenChange(false)}>
                Cancel
              </Button>
              <Button disabled={!canImport} onClick={onImport}>
                Import
              </Button>
            </>
          )}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
