"use client";

import * as React from "react";
import { useRouter, useSearchParams, usePathname } from "next/navigation";
import { toast } from "sonner";

import { StatCards, type StatFilter } from "./stat-cards";
import { Filters } from "./filters";
import { DataTable, getDefaultColumnVisibility, ALL_TOGGLEABLE_COLUMNS } from "./data-table";
import { SelectionToolbar } from "./selection-toolbar";
import { UploadModal, type UploadingFile } from "./upload-modal";
import { createColumns, COPYABLE_FIELDS, type ResponseCard } from "./columns";
import { useUserProfile } from "@/lib/user-profile";
import { can } from "@/lib/permissions";

const VISIT_TYPE_OPTIONS = [
  "First/Second Time Guest",
  "Update My Information",
];

const ATTENDANCE_OPTIONS = [
  "Less than 6 months",
  "6 Months - 1 Year",
  "1-3 Years",
  "4-6 Years",
  "7+ Years",
];

const SERVICE_OPTIONS = ["A", "B", "C", "D"];

function deriveStatFilter(params: URLSearchParams): StatFilter {
  if (params.get("assignedToId") === "me") return "my_cards";
  if (params.get("ocrStatus") === "complete") return "complete";
  if (params.get("ocrStatus") === "error") return "error";
  if (params.get("reviewStatus") === "unreviewed") return "unreviewed";
  return null;
}

export function DashboardContent() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const pathname = usePathname();
  const { role } = useUserProfile();
  const canUpload = can(role, "uploads.create");
  const canEditCards = can(role, "cards.edit");
  const canReview = can(role, "cards.review");
  const canReprocess = can(role, "cards.reprocess");
  const canDelete = can(role, "cards.delete");
  const canAssign = can(role, "cards.assign");

  const page = parseInt(searchParams.get("page") || "1");
  const limit = parseInt(searchParams.get("limit") || "20");
  const sortBy = searchParams.get("sortBy") || "createdAt";
  const sortOrder = (searchParams.get("sortOrder") || "desc") as "asc" | "desc";

  const [data, setData] = React.useState<ResponseCard[]>([]);
  const [total, setTotal] = React.useState(0);
  const [loading, setLoading] = React.useState(true);
  const [columnVisibility, setColumnVisibility] = React.useState<Record<string, boolean>>(getDefaultColumnVisibility);
  const [selectedIds, setSelectedIds] = React.useState<string[]>([]);
  const [uploadModalOpen, setUploadModalOpen] = React.useState(false);
  const [uploadingRows, setUploadingRows] = React.useState<UploadingFile[]>([]);
  const [activeStatFilter, setActiveStatFilter] = React.useState<StatFilter>(() => deriveStatFilter(searchParams));
  const [globalDragOver, setGlobalDragOver] = React.useState(false);
  const [initialFiles, setInitialFiles] = React.useState<File[]>([]);

  const fetchCards = React.useCallback(async () => {
    setLoading(true);
    try {
      const params = new URLSearchParams(searchParams.toString());
      if (!params.has("page")) params.set("page", "1");
      if (!params.has("limit")) params.set("limit", "20");

      const res = await fetch(`/api/cards?${params.toString()}`);
      if (!res.ok) throw new Error("Failed to fetch cards");
      const json = await res.json();

      const cards = json.cards.map((card: Record<string, unknown>) => ({
        ...card,
        createdAt: card.createdAt as string,
        frontImageUrl: card.frontImagePath
          ? `/api/images/${card.frontImagePath}`
          : null,
        backImageUrl: card.backImagePath
          ? `/api/images/${card.backImagePath}`
          : null,
      }));

      setData(cards);
      setTotal(json.total);
    } catch {
      toast.error("Failed to load cards");
    } finally {
      setLoading(false);
    }
  }, [searchParams]);

  React.useEffect(() => {
    fetchCards();
  }, [fetchCards]);

  React.useEffect(() => {
    setActiveStatFilter(deriveStatFilter(searchParams));
  }, [searchParams]);

  const updateUrl = React.useCallback(
    (updates: Record<string, string | number | undefined>) => {
      const params = new URLSearchParams(searchParams.toString());
      for (const [key, value] of Object.entries(updates)) {
        if (value === undefined || value === "") {
          params.delete(key);
        } else {
          params.set(key, String(value));
        }
      }
      router.push(`${pathname}?${params.toString()}`);
    },
    [router, searchParams, pathname]
  );

  const handleStatFilterChange = React.useCallback(
    (filter: StatFilter) => {
      setActiveStatFilter(filter);
      const params = new URLSearchParams(searchParams.toString());

      params.delete("ocrStatus");
      params.delete("reviewStatus");
      params.delete("assignedToId");

      if (filter === "complete") {
        params.set("ocrStatus", "complete");
      } else if (filter === "error") {
        params.set("ocrStatus", "error");
      } else if (filter === "unreviewed") {
        params.set("reviewStatus", "unreviewed");
      } else if (filter === "my_cards") {
        params.set("assignedToId", "me");
      }

      params.set("page", "1");
      router.push(`${pathname}?${params.toString()}`);
    },
    [router, searchParams, pathname]
  );

  const handleBulkAction = async (
    ids: string[],
    action: "reviewed" | "exported" | "delete"
  ) => {
    try {
      if (action === "delete") {
        await Promise.all(
          ids.map((id) => fetch(`/api/cards/${id}`, { method: "DELETE" }))
        );
        toast.success(`Deleted ${ids.length} card(s)`);
      } else {
        await Promise.all(
          ids.map((id) =>
            fetch(`/api/cards/${id}`, {
              method: "PUT",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ reviewStatus: action }),
            })
          )
        );
        toast.success(`Marked ${ids.length} card(s) as ${action}`);
      }
      fetchCards();
    } catch {
      toast.error("Bulk action failed");
    }
  };

  const handleBulkReprocess = async (ids: string[]) => {
    try {
      const res = await fetch("/api/cards/reprocess-batch", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ids }),
      });
      const data = await res.json();
      if (res.ok) {
        toast.success(
          `Reprocessing ${data.queued} card(s)${data.skipped > 0 ? ` (${data.skipped} skipped)` : ""}`
        );
        setSelectedIds([]);
        fetchCards();
      } else {
        toast.error(data.error || "Batch reprocess failed");
      }
    } catch {
      toast.error("Failed to start batch reprocessing");
    }
  };

  const handleBulkSyncMonday = async (ids: string[]) => {
    try {
      toast.info(`Syncing ${ids.length} card(s) to Monday.com…`);
      const res = await fetch("/api/integrations/monday/sync-all", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ cardIds: ids, mode: "all" }),
      });
      const result = await res.json();
      if (!res.ok) {
        toast.error(result.error || "Monday.com sync failed");
        return;
      }
      const parts: string[] = [];
      if (result.created > 0) parts.push(`${result.created} pushed`);
      if (result.updated > 0) parts.push(`${result.updated} updated`);
      if (result.failed > 0) parts.push(`${result.failed} failed`);
      if (result.failed > 0 && result.synced === 0) {
        toast.error(`Monday.com: ${parts.join(", ")}`, {
          description: result.errors?.[0],
        });
      } else if (result.failed > 0) {
        toast.warning(`Monday.com: ${parts.join(", ")}`);
      } else if (result.synced === 0) {
        toast.info("No eligible cards to sync");
      } else {
        toast.success(`Monday.com: ${parts.join(", ")}`);
      }
      fetchCards();
    } catch {
      toast.error("Failed to sync to Monday.com");
    }
  };

  const handleAssign = async (ids: string[], userId: string) => {
    try {
      const res = await fetch("/api/cards/assign", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ cardIds: ids, assignToUserId: userId }),
      });
      const result = await res.json();
      if (!res.ok) {
        toast.error(result.error || "Assignment failed");
        return;
      }
      toast.success(`Assigned ${result.assigned} card(s) to ${result.assignee}`);
      setSelectedIds([]);
      fetchCards();
    } catch {
      toast.error("Failed to assign cards");
    }
  };

  const handleExportCsv = () => {
    if (data.length === 0) {
      toast.error("No data to export");
      return;
    }

    const visibleCols = ALL_TOGGLEABLE_COLUMNS.filter(
      (col) => columnVisibility[col.id] !== false
    );

    if (visibleCols.length === 0) {
      toast.error("No columns visible to export");
      return;
    }

    const headers = visibleCols.map((c) => c.label);
    const rows = data.map((card) =>
      visibleCols.map((col) => {
        const val = (card as Record<string, unknown>)[col.id];
        if (val == null) return "";
        if (typeof val === "boolean") return val ? "Yes" : "No";
        if (Array.isArray(val)) return val.filter(Boolean).join(", ");
        return String(val);
      })
    );

    const csv = [headers, ...rows]
      .map((r) => r.map((v) => `"${v.replace(/"/g, '""')}"`).join(","))
      .join("\n");

    const blob = new Blob([csv], { type: "text/csv" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `response-cards-${new Date().toISOString().split("T")[0]}.csv`;
    a.click();
    URL.revokeObjectURL(url);
    toast.success(`Exported ${data.length} rows × ${visibleCols.length} columns`);
  };

  const columns = React.useMemo(
    () =>
      createColumns({
        onViewDetails: (card) => router.push(`/cards/${card.id}`),
        onMarkReviewed: canReview
          ? async (card) => {
              await fetch(`/api/cards/${card.id}`, {
                method: "PUT",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ reviewStatus: "reviewed" }),
              });
              toast.success("Marked as reviewed");
              fetchCards();
            }
          : undefined,
        onReprocess: canReprocess
          ? async (card) => {
              try {
                const res = await fetch(`/api/cards/${card.id}/reprocess`, {
                  method: "POST",
                });
                if (!res.ok) {
                  const err = await res.json();
                  throw new Error(err.error || "Failed to start reprocessing");
                }
                toast.success("Reprocessing started");
                fetchCards();
              } catch (err) {
                toast.error(
                  err instanceof Error ? err.message : "Failed to start reprocessing"
                );
              }
            }
          : undefined,
        onDelete: canDelete
          ? async (card) => {
              await fetch(`/api/cards/${card.id}`, { method: "DELETE" });
              toast.success("Card deleted");
              fetchCards();
            }
          : undefined,
      }),
    [router, fetchCards, canReview, canReprocess, canDelete]
  );

  const handleUploadStart = (files: UploadingFile[]) => {
    setUploadingRows((prev) => [...prev, ...files]);
  };

  const handleUploadProgress = (fileId: string, progress: number) => {
    setUploadingRows((prev) =>
      prev.map((f) => (f.id === fileId ? { ...f, progress } : f))
    );
  };

  const handleUploadComplete = (fileId: string) => {
    setUploadingRows((prev) => prev.filter((f) => f.id !== fileId));
    fetchCards();
  };

  const handleUploadError = (fileId: string, error: string) => {
    setUploadingRows((prev) =>
      prev.map((f) =>
        f.id === fileId ? { ...f, status: "error" as const, error } : f
      )
    );
  };

  const selectedRows = React.useMemo(
    () => {
      if (selectedIds.length === 0) return [];
      const idSet = new Set(selectedIds);
      return data.filter((c) => idSet.has(c.id));
    },
    [data, selectedIds]
  );

  React.useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;

      if (e.key === "Escape" && selectedIds.length > 0) {
        setSelectedIds([]);
        return;
      }

      if ((e.metaKey || e.ctrlKey) && e.key === "c" && selectedIds.length > 0) {
        e.preventDefault();
        const fields = COPYABLE_FIELDS.filter((f) =>
          f.field === "name" || f.field === "email"
        );
        const values = selectedRows.map((row) =>
          fields.map((f) => {
            const v = row[f.field];
            return v != null ? String(v) : "";
          }).join("\t")
        );
        const header = fields.map((f) => f.label).join("\t");
        navigator.clipboard.writeText([header, ...values].join("\n")).then(() => {
          toast.success(`Copied ${selectedRows.length} row(s) to clipboard`);
        });
        return;
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [selectedIds, selectedRows]);

  React.useEffect(() => {
    const handleOpenUpload = () => setUploadModalOpen(true);
    window.addEventListener("open-upload-modal", handleOpenUpload);
    return () => window.removeEventListener("open-upload-modal", handleOpenUpload);
  }, []);

  React.useEffect(() => {
    if (!canUpload) return;
    const handleDragOver = (e: DragEvent) => {
      e.preventDefault();
      if (e.dataTransfer?.types.includes("Files")) {
        setGlobalDragOver(true);
      }
    };
    const handleDragLeave = (e: DragEvent) => {
      if (e.relatedTarget === null) {
        setGlobalDragOver(false);
      }
    };
    const handleDrop = (e: DragEvent) => {
      e.preventDefault();
      setGlobalDragOver(false);
      if (e.dataTransfer?.files.length) {
        setInitialFiles(Array.from(e.dataTransfer.files));
        setUploadModalOpen(true);
      }
    };

    document.addEventListener("dragover", handleDragOver);
    document.addEventListener("dragleave", handleDragLeave);
    document.addEventListener("drop", handleDrop);

    return () => {
      document.removeEventListener("dragover", handleDragOver);
      document.removeEventListener("dragleave", handleDragLeave);
      document.removeEventListener("drop", handleDrop);
    };
  }, [canUpload]);

  const openUpload = React.useCallback(() => {
    setUploadModalOpen(true);
  }, []);

  const FILTER_PARAM_KEYS = ["search", "ocrStatus", "reviewStatus", "visitType", "assignedToId", "attendanceDuration", "serviceAttended", "eventId", "locationId"];
  const hasActiveFilters = FILTER_PARAM_KEYS.some((k) => searchParams.has(k));
  const clearFilters = React.useCallback(() => {
    router.push(pathname);
  }, [router, pathname]);

  return (
    <div className="space-y-6">
      {/* Global drag overlay */}
      {globalDragOver && (
        <div className="pointer-events-none fixed inset-0 z-50 flex items-center justify-center bg-primary/5 backdrop-blur-sm">
          <div className="rounded-2xl border-2 border-dashed border-primary/40 bg-background/80 px-12 py-8 text-center shadow-xl">
            <p className="text-lg font-semibold">Drop files to upload</p>
            <p className="mt-1 text-sm text-muted-foreground">
              PDF, JPEG, PNG, WebP
            </p>
          </div>
        </div>
      )}

      {/* Page title */}
      <div>
        <h1 className="text-2xl font-bold tracking-tight sm:text-3xl">
          Response Cards
        </h1>
        <p className="mt-1 text-sm text-muted-foreground">
          Manage and review scanned response cards
        </p>
      </div>

      {/* Stat cards as filters */}
      <StatCards
        activeFilter={activeStatFilter}
        onFilterChange={handleStatFilterChange}
      />

      {/* Filter toolbar */}
      <Filters
        visitTypeOptions={VISIT_TYPE_OPTIONS}
        attendanceDurationOptions={ATTENDANCE_OPTIONS}
        serviceAttendedOptions={SERVICE_OPTIONS}
        onExportCsv={handleExportCsv}
        onUploadClick={canUpload ? openUpload : undefined}
        columnVisibility={columnVisibility}
        onColumnVisibilityChange={setColumnVisibility}
        showAssignedToFilter
      />

      {/* Data table */}
      <div className={loading ? "opacity-60 transition-opacity" : ""}>
        <DataTable
          data={data}
          columns={columns}
          totalCount={total}
          page={page}
          limit={limit}
          sortBy={sortBy}
          sortOrder={sortOrder}
          columnVisibility={columnVisibility}
          onColumnVisibilityChange={setColumnVisibility}
          onPageChange={(p) => updateUrl({ page: p })}
          onLimitChange={(l) => updateUrl({ limit: l, page: 1 })}
          onSortChange={(sb, so) => updateUrl({ sortBy: sb, sortOrder: so })}
          uploadingRows={uploadingRows.filter((f) => f.status === "uploading")}
          onSelectionChange={setSelectedIds}
          hasActiveFilters={hasActiveFilters}
          onClearFilters={clearFilters}
          onUploadClick={canUpload ? openUpload : undefined}
        />
      </div>

      {/* Floating selection toolbar */}
      <SelectionToolbar
        selectedIds={selectedIds}
        selectedRows={selectedRows}
        onMarkReviewed={canReview ? (ids) => handleBulkAction(ids, "reviewed") : undefined}
        onMarkExported={canEditCards ? (ids) => handleBulkAction(ids, "exported") : undefined}
        onReprocess={canReprocess ? handleBulkReprocess : undefined}
        onSyncMonday={canReprocess ? handleBulkSyncMonday : undefined}
        onAssign={canAssign ? handleAssign : undefined}
        onDelete={canDelete ? (ids) => handleBulkAction(ids, "delete") : undefined}
        onClear={() => setSelectedIds([])}
      />

      {/* Upload modal */}
      <UploadModal
        open={uploadModalOpen}
        onOpenChange={(open) => {
          setUploadModalOpen(open);
          if (!open) setInitialFiles([]);
        }}
        initialFiles={initialFiles}
        onUploadStart={handleUploadStart}
        onUploadProgress={handleUploadProgress}
        onUploadComplete={handleUploadComplete}
        onUploadError={handleUploadError}
      />
    </div>
  );
}
