"use client";

import * as React from "react";

import {
  getCoreRowModel,
  useReactTable,
  type Header,
} from "@tanstack/react-table";
import { useRouter } from "next/navigation";
import { toast } from "sonner";

import {
  DataTablePagination,
  DataTableShell,
  useServerPagination,
  type ServerPaginationMeta,
} from "@/app/dashboard/_components/data-table";
import { useMasterListFilters } from "@/hooks/use-master-list-filters";
import { useDebouncedValue } from "@/hooks/use-debounced-value";
import { requestDashboardApi } from "@/lib/dashboard-api-client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { TableCell } from "@/components/ui/table";

import { createCurrencyColumns } from "./columns";
import {
  CURRENCY_STATUS_OPTIONS,
  YES_NO_FILTER_OPTIONS,
  type CurrencyFilters,
  type CurrencyRow,
} from "./schema";

const PRIORITY_SAVE_HREF = "/dashboard/master/currencies/priority";

type Props = {
  data: CurrencyRow[];
  initialFilters: CurrencyFilters;
  pagination: ServerPaginationMeta;
};

type TextDrafts = Pick<CurrencyFilters, "name" | "code">;

export function CurrenciesTable({
  data,
  initialFilters,
  pagination: serverPagination,
}: Props) {
  const router = useRouter();
  const { filters, setFilter } = useMasterListFilters(initialFilters, [
    "name",
    "code",
    "isDefault",
    "status",
  ] as const);
  const [drafts, setDrafts] = React.useState<TextDrafts>({
    name: filters.name,
    code: filters.code,
  });
  const committedTextRef = React.useRef<TextDrafts>({
    name: filters.name,
    code: filters.code,
  });
  const [priorities, setPriorities] = React.useState<Record<number, number>>(() =>
    Object.fromEntries(data.map((row) => [row.id, row.priority])),
  );
  const { pagination, onPaginationChange, totalCount } = useServerPagination(serverPagination);
  const debouncedName = useDebouncedValue(drafts.name, 300);
  const debouncedCode = useDebouncedValue(drafts.code, 300);

  React.useEffect(() => {
    setDrafts((draft) => ({
      name:
        draft.name === committedTextRef.current.name || draft.name === filters.name
          ? filters.name
          : draft.name,
      code:
        draft.code === committedTextRef.current.code || draft.code === filters.code
          ? filters.code
          : draft.code,
    }));
    committedTextRef.current = {
      name: filters.name,
      code: filters.code,
    };
  }, [filters.name, filters.code]);

  React.useEffect(() => {
    if (debouncedName !== filters.name) setFilter("name", debouncedName);
  }, [debouncedName, filters.name, setFilter]);

  React.useEffect(() => {
    if (debouncedCode !== filters.code) setFilter("code", debouncedCode);
  }, [debouncedCode, filters.code, setFilter]);

  React.useEffect(() => {
    setPriorities(Object.fromEntries(data.map((row) => [row.id, row.priority])));
  }, [data]);

  const columns = React.useMemo(
    () =>
      createCurrencyColumns({
        priorities,
        onPriorityChange: (id, priority) => {
          setPriorities((prev) => ({ ...prev, [id]: priority }));
        },
      }),
    [priorities],
  );

  const table = useReactTable({
    data,
    columns,
    state: { pagination },
    onPaginationChange,
    manualPagination: true,
    pageCount: serverPagination.pageCount,
    rowCount: totalCount,
    getCoreRowModel: getCoreRowModel(),
    getRowId: (row) => String(row.id),
  });

  const updateDraft = <K extends keyof TextDrafts>(key: K, value: TextDrafts[K]) => {
    setDrafts((prev) => ({ ...prev, [key]: value }));
  };

  const updateFilter = <K extends keyof CurrencyFilters>(key: K, value: CurrencyFilters[K]) => {
    setFilter(key, value);
  };

  const handlePrioritySave = async () => {
    try {
      const response = await requestDashboardApi<{ message?: string }>({
        url: PRIORITY_SAVE_HREF,
        method: "POST",
        body: { priority: priorities },
        fallbackError: "Currency priority could not be updated.",
      });
      toast.success(response.message ?? "Priority successfully updated!");
      router.refresh();
    } catch (error) {
      toast.error(error instanceof Error ? error.message : "Currency priority could not be updated.");
    }
  };

  return (
    <div className="space-y-4">
      <DataTableShell
        table={table}
        columnCount={columns.length}
        emptyMessage="No currencies match your filters."
        renderFilterCell={(header) => renderFilterCell(header, filters, drafts, updateFilter, updateDraft)}
      />

      <DataTablePagination
        table={table}
        totalRows={totalCount}
        itemNoun="currency"
        idPrefix="currencies"
      />

      <div className="flex justify-end border-t pt-4">
        <Button type="button" onClick={handlePrioritySave}>
          Update priority
        </Button>
      </div>
    </div>
  );
}

function renderFilterCell(
  header: Header<CurrencyRow, unknown>,
  filters: CurrencyFilters,
  drafts: TextDrafts,
  updateFilter: <K extends keyof CurrencyFilters>(key: K, value: CurrencyFilters[K]) => void,
  updateDraft: <K extends keyof TextDrafts>(key: K, value: TextDrafts[K]) => void,
) {
  const columnId = header.column.id;

  if (columnId === "name") {
    return (
      <TableCell key={header.id} className="p-2">
        <Input
          value={drafts.name}
          onChange={(event) => updateDraft("name", event.target.value)}
          placeholder="Currency name"
          className="h-8 bg-background text-xs"
        />
      </TableCell>
    );
  }

  if (columnId === "code") {
    return (
      <TableCell key={header.id} className="p-2">
        <Input
          value={drafts.code}
          onChange={(event) => updateDraft("code", event.target.value.toUpperCase())}
          placeholder="Code"
          className="h-8 bg-background font-mono text-xs uppercase"
        />
      </TableCell>
    );
  }

  if (columnId === "isDefault") {
    return (
      <TableCell key={header.id} className="p-2">
        <Select
          value={filters.isDefault}
          onValueChange={(value) => updateFilter("isDefault", value)}
        >
          <SelectTrigger className="h-8 bg-background text-xs">
            <SelectValue placeholder="Is default" />
          </SelectTrigger>
          <SelectContent>
            {YES_NO_FILTER_OPTIONS.map((option) => (
              <SelectItem key={option.value} value={option.value}>
                {option.label}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
      </TableCell>
    );
  }

  if (columnId === "status") {
    return (
      <TableCell key={header.id} className="p-2">
        <Select value={filters.status} onValueChange={(value) => updateFilter("status", value)}>
          <SelectTrigger className="h-8 bg-background text-xs">
            <SelectValue placeholder="Status" />
          </SelectTrigger>
          <SelectContent>
            {CURRENCY_STATUS_OPTIONS.map((option) => (
              <SelectItem key={option.value} value={option.value}>
                {option.label}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
      </TableCell>
    );
  }

  return null;
}
