"use client";
import { useEffect, useRef, useState } from "react";
import {
  exportData,
  type ExportRow,
  type ExportFormat,
} from "@/lib/export-data";
import { Checkbox } from "@/components/ui/checkbox";
import { useRouter, useSearchParams } from "next/navigation";
import {
  Search,
  RefreshCw,
  SlidersHorizontal,
  Download,
  Plus,
  X,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
  SearchableFilterField,
  type SearchableFilterOption,
} from "./searchable-filter-field";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from "@/components/ui/dialog";
import { FormField, SelectField } from "./form-field";
import { useQueryUpdate } from "./query-controls";
type FilterField = {
  name: string;
  label: string;
  options?: readonly (string | SearchableFilterOption)[];
  defaultValue?: string;
  includeAllOption?: boolean;
  type?: "text" | "number" | "date";
  section?: string;
  searchable?: boolean;
  placeholder?: string;
  helperText?: string;
};

function displayFilterValue(field: FilterField, value: string) {
  const option = field.options?.find((item) =>
    typeof item === "string" ? item === value : item.value === value,
  );
  if (!option) return value;
  return typeof option === "string" ? option : option.label;
}
export function TableToolbar({
  title,
  description,
  searchKey = "query",
  searchPlaceholder,
  fields,
  exportRows,
  exportIds,
  onCreate,
  createLabel,
  showSearch = true,
  showFilters = true,
  showExport = true,
  searchDebounceMs,
  refreshNotice = "Mock data refreshed",
  onRefresh,
}: {
  title: string;
  description: string;
  searchKey?: string;
  searchPlaceholder?: string;
  fields: FilterField[];
  exportRows: ExportRow[];
  exportIds: string[];
  onCreate?: () => void;
  createLabel?: string;
  showSearch?: boolean;
  showFilters?: boolean;
  showExport?: boolean;
  searchDebounceMs?: number;
  refreshNotice?: string;
  onRefresh?: () => void;
}) {
  const router = useRouter();
  const params = useSearchParams();
  const update = useQueryUpdate();
  const [filter, setFilter] = useState(false);
  const [download, setDownload] = useState(false);
  const [notice, setNotice] = useState("");
  const currentSearch = params.get(searchKey) ?? "";
  const [searchState, setSearchState] = useState(() => ({
    source: currentSearch,
    value: currentSearch,
  }));
  const searchValue =
    searchState.source === currentSearch ? searchState.value : currentSearch;
  const searchTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
  const [filterDraft, setFilterDraft] = useState<Record<string, string>>({});
  const [format, setFormat] = useState<ExportFormat>("pdf");
  const [scope, setScope] = useState("page");
  const [selected, setSelected] = useState<string[]>([]);
  const [exporting, setExporting] = useState(false);
  const [exportError, setExportError] = useState("");
  const selectedRows = exportRows.filter((_, i) =>
    selected.includes(exportIds[i]),
  );
  const values = fields.filter((field) => params.get(field.name));
  useEffect(
    () => () => {
      if (searchTimeout.current) clearTimeout(searchTimeout.current);
    },
    [],
  );
  function changeSearch(value: string) {
    setSearchState({ source: currentSearch, value });
    if (searchDebounceMs === undefined) return;
    if (searchTimeout.current) clearTimeout(searchTimeout.current);
    searchTimeout.current = setTimeout(() => {
      update({ [searchKey]: value, paginate_page: null });
      searchTimeout.current = null;
    }, searchDebounceMs);
  }
  function startFiltering() {
    setFilterDraft(
      Object.fromEntries(
        fields.map((field) => [
          field.name,
          params.get(field.name) ?? field.defaultValue ?? "",
        ]),
      ),
    );
    setFilter(true);
  }
  async function downloadData() {
    setExporting(true);
    setExportError("");
    try {
      await exportData(
        title,
        scope === "page" ? exportRows : selectedRows,
        format,
      );
      setDownload(false);
    } catch {
      setExportError("Unable to prepare this download. Please try again.");
    } finally {
      setExporting(false);
    }
  }
  return (
    <>
      <div className="p-5 pb-4">
        <div className="flex flex-wrap items-center justify-between gap-4">
          <div>
            <h2 className="text-xl font-semibold">{title}</h2>
            <p className="mt-1 text-muted-foreground">{description}</p>
          </div>
          <div className="flex flex-wrap items-center gap-2">
            {showSearch && (
              <form
                className="relative min-w-0 flex-1 sm:w-[250px]"
                onSubmit={(event) => {
                  event.preventDefault();
                  if (searchTimeout.current) {
                    clearTimeout(searchTimeout.current);
                    searchTimeout.current = null;
                  }
                  update({
                    [searchKey]: searchValue,
                    paginate_page: null,
                  });
                }}
              >
                <Search
                  aria-hidden="true"
                  className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"
                />
                <Input
                  name="search"
                  aria-label={`Search ${title.toLowerCase()}`}
                  placeholder={searchPlaceholder ?? `Search ${title === "Chargestations" ? "chargepoints" : title.toLowerCase()}...`}
                  value={searchValue}
                  onChange={(event) => changeSearch(event.target.value)}
                  className="pl-10 pr-11"
                />
                {searchDebounceMs === undefined && (
                  <button
                    type="submit"
                    aria-label="Apply search"
                    className="absolute right-0 top-0 flex size-11 items-center justify-center text-muted-foreground"
                  >
                    <Search className="size-5" />
                  </button>
                )}
              </form>
            )}
            <Button
              variant="outline"
              size="icon"
              aria-label="Refresh table"
              onClick={() => {
                if (onRefresh) onRefresh();
                else router.refresh();
                setNotice(refreshNotice);
              }}
            >
              <RefreshCw className="size-5" />
            </Button>
            {showFilters && (
              <Button
                variant="outline"
                size="icon"
                aria-label={`Filter ${title.toLowerCase()}${values.length ? `, ${values.length} active` : ""}`}
                onClick={startFiltering}
              >
                <span className="relative inline-flex">
                  <SlidersHorizontal className="size-5" />
                  {values.length > 0 && (
                    <span className="absolute -top-2 -right-2 flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-semibold text-primary-foreground">
                      {values.length}
                    </span>
                  )}
                </span>
              </Button>
            )}
            {showExport && (
              <Button
                variant="outline"
                size="icon"
                aria-label={`Download ${title.toLowerCase()}`}
                onClick={() => setDownload(true)}
              >
                <Download className="size-5" />
              </Button>
            )}
            {onCreate && (
              <Button
                size="icon"
                className="w-14"
              aria-label={`Create ${createLabel ?? (title === "Chargestations" ? "chargepoint" : "location")}`}
                onClick={onCreate}
              >
                <Plus className="size-5" />
              </Button>
            )}
          </div>
        </div>
        {values.length > 0 && (
          <div className="mt-5 flex flex-wrap items-center gap-2 border-t pt-3">
            <span className="font-medium">Active Filters:</span>
            {values.map((field) => (
              <button
                key={field.name}
                className="flex items-center gap-1 rounded-full border bg-muted px-3 py-1.5"
                aria-label={`Remove ${field.label} filter`}
                onClick={() =>
                  update({ [field.name]: null, paginate_page: null })
                }
              >
                {field.label}: {displayFilterValue(field, params.get(field.name) ?? "")}
                <X className="size-3" />
              </button>
            ))}
          </div>
        )}
        {notice && (
          <p role="status" className="mt-2 text-xs text-muted-foreground">
            {notice}
          </p>
        )}
      </div>
      {showFilters && (
        <Dialog
          open={filter}
          onOpenChange={(open) => {
            if (open) startFiltering();
            else setFilter(false);
          }}
        >
          <DialogContent className="flex max-h-[90dvh] flex-col gap-0 overflow-hidden p-0 sm:max-w-[840px]">
            <DialogHeader className="shrink-0 border-b px-6 py-5 pr-14">
              <DialogTitle>Filter {title}</DialogTitle>
              <DialogDescription>
                Narrow the results with API-supported fields. You can paste IDs
                directly when suggestions are unavailable.
              </DialogDescription>
            </DialogHeader>
            <form
              className="flex min-h-0 flex-1 flex-col"
              onSubmit={(event) => {
                event.preventDefault();
                const updates: Record<string, string | null> = {
                  paginate_page: null,
                };
                fields.forEach((field) => {
                  updates[field.name] = filterDraft[field.name] ?? "";
                });
                update(updates);
                setFilter(false);
              }}
            >
              <div className="min-h-0 flex-1 space-y-4 overflow-y-auto px-6 py-5">
                    {fields.reduce<{ name: string; fields: FilterField[] }[]>(
                  (groups, field) => {
                    const name = field.section ?? "Filters";
                    const group = groups.find((item) => item.name === name);
                    if (group) group.fields.push(field);
                    else groups.push({ name, fields: [field] });
                    return groups;
                  },
                  [],
                ).map((group) => (
                  <section
                    key={group.name}
                    aria-labelledby={`filter-section-${group.name.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}`}
                    className="rounded-xl border bg-card/50 p-4 sm:p-5"
                  >
                    <h3
                      id={`filter-section-${group.name.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}`}
                      className="text-sm font-semibold tracking-wide"
                    >
                      {group.name}
                    </h3>
                    <div className="mt-4 grid grid-cols-1 gap-x-4 gap-y-5 sm:grid-cols-2">
                      {group.fields.map((field) => {
                        const value =
                          filterDraft[field.name] ?? field.defaultValue ?? "";
                        if (field.searchable) {
                          const options: SearchableFilterOption[] = (
                            field.options ?? []
                          ).map((option) =>
                            typeof option === "string"
                              ? { value: option, label: option }
                              : option,
                          );
                          return (
                            <SearchableFilterField
                              key={field.name}
                              name={field.name}
                              label={field.label}
                              value={value}
                              options={options}
                              placeholder={field.placeholder}
                              helperText={field.helperText}
                              onValueChange={(nextValue) =>
                                setFilterDraft((current) => ({
                                  ...current,
                                  [field.name]: nextValue,
                                }))
                              }
                            />
                          );
                        }
                        if (field.options) {
                          return (
                            <SelectField
                              key={field.name}
                              name={field.name}
                              label={field.label}
                              value={value}
                              onChange={(event) =>
                                setFilterDraft((current) => ({
                                  ...current,
                                  [field.name]: event.target.value,
                                }))
                              }
                              options={
                                field.includeAllOption === false
                                  ? field.options
                                  : [{ value: "", label: "All" }, ...field.options]
                              }
                            />
                          );
                        }
                        return (
                          <div key={field.name} className="min-w-0">
                            <FormField
                              name={field.name}
                              label={field.label}
                              type={field.type ?? "text"}
                              step={field.type === "number" ? "any" : undefined}
                              placeholder={field.placeholder}
                              value={value}
                              onChange={(event) =>
                                setFilterDraft((current) => ({
                                  ...current,
                                  [field.name]: event.target.value,
                                }))
                              }
                            />
                            {field.helperText && (
                              <p className="mt-1.5 text-xs leading-relaxed text-muted-foreground">
                                {field.helperText}
                              </p>
                            )}
                          </div>
                        );
                      })}
                    </div>
                  </section>
                ))}
              </div>
              <DialogFooter className="mx-0 mb-0 shrink-0 rounded-b-xl px-6 py-4 sm:flex-row">
                <Button
                  type="button"
                  variant="outline"
                  onClick={() => {
                    const updates: Record<string, null> = { paginate_page: null };
                    fields.forEach((field) => (updates[field.name] = null));
                    update(updates);
                    setFilter(false);
                  }}
                >
                  Reset
                </Button>
                <Button>Apply Filters</Button>
              </DialogFooter>
            </form>
          </DialogContent>
        </Dialog>
      )}
      {showExport && <Dialog open={download} onOpenChange={setDownload}>
        <DialogContent className="sm:max-w-[584px]">
          <DialogHeader>
            <DialogTitle>Download {title}</DialogTitle>
            <DialogDescription>
              Export the records shown on this page.
            </DialogDescription>
          </DialogHeader>
          <div>
            <p className="field-label">File Type</p>
            <div className="grid grid-cols-3 gap-3">
              {(
                [
                  { value: "pdf", label: "PDF" },
                  { value: "csv", label: "CSV" },
                  { value: "xlsx", label: "Excel" },
                ] as const
              ).map((opt) => (
                <Button
                  key={opt.value}
                  variant={format === opt.value ? "default" : "outline"}
                  aria-pressed={format === opt.value}
                  onClick={() => setFormat(opt.value)}
                >
                  {opt.label}
                </Button>
              ))}
            </div>
          </div>
          <div>
            <p className="field-label">Select Data</p>
            <div className="grid grid-cols-2 gap-3">
              <Button
                variant={scope === "page" ? "default" : "outline"}
                aria-pressed={scope === "page"}
                onClick={() => setScope("page")}
              >
                Current Page
              </Button>
              <Button
                variant={scope === "selected" ? "default" : "outline"}
                aria-pressed={scope === "selected"}
                onClick={() => setScope("selected")}
              >
                Select {title}
              </Button>
            </div>
          </div>
          {scope === "selected" && (
            <div className="max-h-48 divide-y overflow-y-auto rounded-lg border">
              {exportRows.map((row, i) => (
                <label
                  key={exportIds[i]}
                  className="flex items-center gap-3 px-3 py-2"
                >
                  <Checkbox
                    checked={selected.includes(exportIds[i])}
                    onCheckedChange={(checked) =>
                      setSelected((current) =>
                        checked
                          ? [...current, exportIds[i]]
                          : current.filter((id) => id !== exportIds[i]),
                      )
                    }
                  />
                  <span className="truncate">
                    {Object.values(row)[title === "Sessions" ? 1 : 0]}
                  </span>
                </label>
              ))}
            </div>
          )}
          {exportError && (
            <p role="alert" className="text-destructive">
              {exportError}
            </p>
          )}
          <DialogFooter>
            <Button variant="outline" onClick={() => setDownload(false)}>
              Cancel
            </Button>
            <Button
              onClick={downloadData}
              disabled={
                exporting ||
                !exportRows.length ||
                (scope === "selected" && !selectedRows.length)
              }
            >
              {exporting ? "Preparing..." : "Download"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>}
    </>
  );
}
