"use client";

import * as React from "react";
import { format, parseISO } from "date-fns";
import { toast } from "sonner";
import { Plus, Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { DivisionSelect } from "@/components/shared/division-select";
import { TeamSelect } from "@/components/shared/team-select";
import { LeadChannelSelect } from "@/components/shared/lead-channel-select";
import { useLeadChannelsCatalog } from "@/hooks/use-lead-channels-catalog";
import { Can } from "@/components/providers/ability-provider";
import {
  marketingApi,
  useGetMarketingSpendQuery,
  useCreateMarketingSpendMutation,
  useDeleteMarketingSpendMutation,
  type MarketingSpendRow,
} from "@/api/rtk/marketing-api";
import { useAppDispatch } from "@/store/hooks";
import { DeleteDialog } from "@/components/ui/delete-dialog";
import { formatMarketingCurrency } from "./marketing-kpi-card";
import {
  SpendLedgerFilters,
  getDefaultSpendFilters,
  getPreviousMonth,
  spendFiltersToQuery,
  type SpendFiltersState,
} from "./spend-ledger-filters";
import { SpendSummaryCards } from "./spend-summary-cards";
import { cn } from "@/lib/utils";

type SpendFormState = {
  divisionId: string;
  teamId: string;
  amount: string;
  channel: string;
  spentDate: string;
};

type SpendFormErrors = Partial<Record<keyof SpendFormState, string>>;

const EMPTY_FORM: SpendFormState = {
  divisionId: "",
  teamId: "",
  amount: "",
  channel: "",
  spentDate: "",
};

function todayValue(): string {
  return format(new Date(), "yyyy-MM-dd");
}

function formatSpendMonth(spentOn: string): string {
  try {
    return format(parseISO(spentOn), "MMM yyyy");
  } catch {
    return spentOn;
  }
}

function formatFilterPeriod(filters: SpendFiltersState): string {
  return formatSpendMonth(`${filters.month}-01`);
}

type SpendChannelGroup = {
  channelKey: string;
  label: string;
  total: number;
  rows: MarketingSpendRow[];
};

type SpendMonthGroup = {
  monthKey: string;
  label: string;
  total: number;
  channels: SpendChannelGroup[];
};

function normalizeChannelLabel(channel: string | null): string {
  return channel?.trim() || "Unassigned";
}

function groupRowsByMonthAndChannel(rows: MarketingSpendRow[]): SpendMonthGroup[] {
  const monthMap = new Map<string, MarketingSpendRow[]>();

  for (const row of rows) {
    const monthKey = row.spentOn.slice(0, 7);
    const bucket = monthMap.get(monthKey);
    if (bucket) {
      bucket.push(row);
    } else {
      monthMap.set(monthKey, [row]);
    }
  }

  return Array.from(monthMap.entries())
    .sort(([a], [b]) => b.localeCompare(a))
    .map(([monthKey, monthRows]) => {
      const channelMap = new Map<string, MarketingSpendRow[]>();

      for (const row of monthRows) {
        const channelKey = normalizeChannelLabel(row.channel);
        const bucket = channelMap.get(channelKey);
        if (bucket) {
          bucket.push(row);
        } else {
          channelMap.set(channelKey, [row]);
        }
      }

      const channels = Array.from(channelMap.entries())
        .sort(([a], [b]) => a.localeCompare(b))
        .map(([channelKey, channelRows]) => ({
          channelKey,
          label: channelKey,
          total: channelRows.reduce((sum, row) => sum + row.amount, 0),
          rows: channelRows,
        }));

      return {
        monthKey,
        label: formatSpendMonth(`${monthKey}-01`),
        total: monthRows.reduce((sum, row) => sum + row.amount, 0),
        channels,
      };
    });
}

function SpendField({
  label,
  hint,
  error,
  children,
}: {
  label: string;
  hint?: string;
  error?: string;
  children: React.ReactNode;
}) {
  return (
    <div className="space-y-2">
      <Label className="text-[11px] font-bold text-gray-400 uppercase tracking-widest">
        {label}
      </Label>
      {children}
      {error ? (
        <p className="text-[12px] text-red-500">{error}</p>
      ) : hint ? (
        <p className="text-[12px] text-gray-500">{hint}</p>
      ) : null}
    </div>
  );
}

function isNotFoundMutationError(error: unknown): boolean {
  return (
    typeof error === "object" &&
    error != null &&
    "status" in error &&
    (error as { status?: unknown }).status === 404
  );
}

function validateForm(form: SpendFormState): SpendFormErrors {
  const errors: SpendFormErrors = {};
  if (!form.divisionId) errors.divisionId = "Select a division";
  if (!form.amount.trim()) {
    errors.amount = "Enter a spending amount";
  } else {
    const amount = parseFloat(form.amount);
    if (!Number.isFinite(amount) || amount <= 0) {
      errors.amount = "Enter an amount greater than zero";
    }
  }
  if (!form.channel) errors.channel = "Select a channel";
  if (!form.spentDate) errors.spentDate = "Select a spend date";
  return errors;
}

export function SpendLedger() {
  const dispatch = useAppDispatch();
  const [page, setPage] = React.useState(1);
  const [appliedFilters, setAppliedFilters] = React.useState<SpendFiltersState>(
    () => getDefaultSpendFilters(),
  );

  const queryArgs = React.useMemo(
    () => spendFiltersToQuery(appliedFilters, page, 10),
    [appliedFilters, page],
  );

  const previousMonthQueryArgs = React.useMemo(
    () =>
      spendFiltersToQuery(
        {
          ...appliedFilters,
          month: getPreviousMonth(appliedFilters.month),
        },
        1,
        1,
      ),
    [appliedFilters],
  );

  const previousMonthLabel = React.useMemo(
    () => formatSpendMonth(`${getPreviousMonth(appliedFilters.month)}-01`),
    [appliedFilters.month],
  );

  const { data, isLoading, isFetching, refetch } =
    useGetMarketingSpendQuery(queryArgs);
  const { data: previousMonthData, isLoading: isPreviousMonthLoading } =
    useGetMarketingSpendQuery(previousMonthQueryArgs);
  const [createSpend, { isLoading: creating }] =
    useCreateMarketingSpendMutation();
  const [deleteSpend] = useDeleteMarketingSpendMutation();

  const [dialogOpen, setDialogOpen] = React.useState(false);
  const [deleteId, setDeleteId] = React.useState<string | null>(null);
  const [form, setForm] = React.useState<SpendFormState>(EMPTY_FORM);
  const [errors, setErrors] = React.useState<SpendFormErrors>({});

  const { items: leadChannels } = useLeadChannelsCatalog({ skip: !dialogOpen });

  const monthGroups = React.useMemo(
    () => groupRowsByMonthAndChannel(data?.data ?? []),
    [data?.data],
  );

  const resetForm = React.useCallback(() => {
    setForm(EMPTY_FORM);
    setErrors({});
  }, []);

  const openDialog = () => {
    setForm({ ...EMPTY_FORM, spentDate: todayValue() });
    setErrors({});
    setDialogOpen(true);
  };

  const handleDialogOpenChange = (open: boolean) => {
    setDialogOpen(open);
    if (!open) resetForm();
  };

  const handleApplyFilters = (filters: SpendFiltersState) => {
    setAppliedFilters({ ...filters });
    setPage(1);
  };

  const handleClearFilters = React.useCallback(() => {
    setPage(1);
  }, []);

  const handleCreate = async (event: React.FormEvent) => {
    event.preventDefault();
    const nextErrors = validateForm(form);
    setErrors(nextErrors);
    if (Object.keys(nextErrors).length > 0) return;

    try {
      await createSpend({
        divisionId: form.divisionId,
        teamId: form.teamId || undefined,
        spentOn: form.spentDate,
        amount: parseFloat(form.amount),
        channel: form.channel,
      }).unwrap();
      toast.success("Spend recorded");
      handleDialogOpenChange(false);
      refetch();
    } catch {
      toast.error("Could not record spend");
    }
  };

  const handleDelete = async () => {
    if (!deleteId) return;
    const removingId = deleteId;
    setDeleteId(null);

    const listPatch = dispatch(
      marketingApi.util.updateQueryData("getMarketingSpend", queryArgs, (draft) => {
        const removed = draft.data.find((row) => row.id === removingId);
        draft.data = draft.data.filter((row) => row.id !== removingId);
        if (removed) {
          draft.total = Math.max(0, draft.total - 1);
          draft.summary.entryCount = Math.max(0, draft.summary.entryCount - 1);
          draft.summary.totalAmount = Math.max(
            0,
            draft.summary.totalAmount - removed.amount,
          );
        }
      }),
    );

    try {
      await deleteSpend(removingId).unwrap();
      toast.success("Spend entry removed");
    } catch (error) {
      listPatch.undo();
      const notFound = isNotFoundMutationError(error);
      if (notFound) {
        dispatch(
          marketingApi.util.updateQueryData(
            "getMarketingSpend",
            queryArgs,
            (draft) => {
              draft.data = draft.data.filter((row) => row.id !== removingId);
            },
          ),
        );
        toast.success("Spend entry already removed");
      } else {
        toast.error("Could not remove spend entry");
        return;
      }
    }

    void refetch();
  };

  return (
    <div className="flex flex-col gap-4">
      <SpendLedgerFilters
        value={appliedFilters}
        onApply={handleApplyFilters}
        onClearFilters={handleClearFilters}
      />

      <SpendSummaryCards
        summary={data?.summary}
        periodLabel={formatFilterPeriod(appliedFilters)}
        isLoading={isLoading || isFetching}
        showPreviousMonth
        previousMonthSummary={previousMonthData?.summary}
        previousMonthLabel={previousMonthLabel}
        isPreviousMonthLoading={isPreviousMonthLoading}
      />

      <div className="rounded-2xl border border-white/40 bg-white/50 backdrop-blur-[15px] p-4 md:p-5">
        <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 mb-4">
          <div>
            <h2 className="font-['Lexend'] text-lg font-bold text-[#1d1d39]">
              Spend ledger
            </h2>
            <p className="text-[12px] text-gray-500 mt-1">
              {formatFilterPeriod(appliedFilters)} · grouped by month → channel
            </p>
          </div>
          <Can action="update" subject="marketing">
            <Button
              type="button"
              onClick={openDialog}
              className="rounded-xl gap-2 shrink-0"
            >
              <Plus size={16} />
              Record spend
            </Button>
          </Can>
        </div>

        {isLoading ? (
          <p className="text-sm text-gray-500 py-8 text-center">Loading spend…</p>
        ) : !data?.data.length ? (
          <p className="text-sm text-gray-500 py-8 text-center">
            No spend entries for the selected filters.
          </p>
        ) : (
          <>
            <Table variant="transparent">
              <TableHeader>
                <TableRow className="hover:bg-transparent cursor-default">
                  <TableHead>Division</TableHead>
                  <TableHead>Team</TableHead>
                  <TableHead>Spending</TableHead>
                  <TableHead>Recorded by</TableHead>
                  <TableHead className="text-right">Actions</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {monthGroups.map((group) => (
                  <React.Fragment key={group.monthKey}>
                    <TableRow className="bg-white/25 hover:bg-white/25 cursor-default">
                      <TableCell
                        colSpan={5}
                        className="py-2.5 font-['Lexend'] text-[12px] font-bold text-[#6C63FF] uppercase tracking-wide"
                      >
                        {group.label}
                        <span className="ml-2 font-normal normal-case text-gray-600">
                          {formatMarketingCurrency(group.total)}
                        </span>
                      </TableCell>
                    </TableRow>
                    {group.channels.map((channel) => (
                      <React.Fragment
                        key={`${group.monthKey}-${channel.channelKey}`}
                      >
                        <TableRow className="bg-white/10 hover:bg-white/10 cursor-default">
                          <TableCell
                            colSpan={5}
                            className="py-2 pl-6 font-['Lexend'] text-[11px] font-semibold text-gray-700"
                          >
                            {channel.label}
                            <span className="ml-2 font-normal text-gray-500">
                              {formatMarketingCurrency(channel.total)}
                            </span>
                          </TableCell>
                        </TableRow>
                        {channel.rows.map((row) => (
                          <TableRow key={row.id} className="cursor-default">
                            <TableCell className="pl-8">
                              {row.divisionName ?? "—"}
                            </TableCell>
                            <TableCell>{row.teamName ?? "All teams"}</TableCell>
                            <TableCell className="tabular-nums font-medium">
                              {formatMarketingCurrency(row.amount)}
                            </TableCell>
                            <TableCell>{row.recordedByName ?? "—"}</TableCell>
                            <TableCell className="text-right">
                              <Can action="update" subject="marketing">
                                <Button
                                  type="button"
                                  variant="ghost"
                                  size="icon"
                                  onClick={() => setDeleteId(row.id)}
                                >
                                  <Trash2 size={15} />
                                </Button>
                              </Can>
                            </TableCell>
                          </TableRow>
                        ))}
                      </React.Fragment>
                    ))}
                  </React.Fragment>
                ))}
              </TableBody>
            </Table>

            {data.totalPages > 1 ? (
              <div className="flex justify-between items-center gap-2 mt-4 pt-2 border-t border-black/5">
                <p className="text-[12px] text-gray-500">
                  Page {data.page} of {data.totalPages} · {data.total} entries
                </p>
                <div className="flex gap-2">
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    disabled={page <= 1}
                    onClick={() => setPage((p) => p - 1)}
                    className="rounded-xl"
                  >
                    Previous
                  </Button>
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    disabled={page >= data.totalPages}
                    onClick={() => setPage((p) => p + 1)}
                    className="rounded-xl"
                  >
                    Next
                  </Button>
                </div>
              </div>
            ) : null}
          </>
        )}
      </div>

      <p className="text-[11px] text-gray-500 font-['Lexend_Deca']">
        Spend is stored by calendar month. Recording on any day in June counts
        toward June; a new month starts a fresh total.
      </p>

      <Dialog open={dialogOpen} onOpenChange={handleDialogOpenChange}>
        <DialogContent className="rounded-2xl max-w-md">
          <DialogHeader>
            <DialogTitle className="text-[18px] font-extrabold text-gray-900 font-['Lexend']">
              Record marketing spend
            </DialogTitle>
            <DialogDescription className="text-[13px] text-gray-500">
              Log marketing spend by division and channel. Any date within a
              month rolls up to that month&apos;s total.
            </DialogDescription>
          </DialogHeader>
          <form onSubmit={handleCreate} className="space-y-4 py-2">
            <SpendField
              label="Division"
              hint="Which division this spend belongs to."
              error={errors.divisionId}
            >
              <DivisionSelect
                value={form.divisionId}
                onValueChange={(value) =>
                  setForm((f) => ({
                    ...f,
                    divisionId: value,
                    teamId: "",
                  }))
                }
                placeholder="Select division"
                aria-label="Division"
                skip={!dialogOpen}
                triggerClassName="h-11 rounded-xl border-gray-200 bg-gray-50"
              />
            </SpendField>

            <SpendField
              label="Team (optional)"
              hint={
                form.divisionId
                  ? "Leave as all teams to apply spend across the division, or pick one team to narrow it."
                  : "Select a division first to choose a team."
              }
              error={errors.teamId}
            >
              <TeamSelect
                value={form.teamId}
                onValueChange={(value) =>
                  setForm((f) => ({ ...f, teamId: value }))
                }
                divisionId={form.divisionId}
                requireDivision
                allowEmpty
                emptyOptionLabel="All teams"
                placeholder="All teams"
                aria-label="Team"
                skip={!dialogOpen}
                triggerClassName={cn(
                  "h-11 rounded-xl border-gray-200 bg-gray-50",
                  !form.divisionId && "opacity-60",
                )}
              />
            </SpendField>

            <SpendField
              label="Spending ($)"
              hint="Total spend in US dollars for the selected month."
              error={errors.amount}
            >
              <div className="relative">
                <span className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-gray-500">
                  $
                </span>
                <Input
                  type="number"
                  min={0}
                  step="0.01"
                  placeholder="2500.00"
                  value={form.amount}
                  onChange={(e) =>
                    setForm((f) => ({ ...f, amount: e.target.value }))
                  }
                  className="h-11 rounded-xl border-gray-200 bg-gray-50 pl-7 focus:bg-white"
                />
              </div>
            </SpendField>

            <SpendField
              label="Channel"
              hint={
                leadChannels.length > 0
                  ? "Marketing channel from Settings → Extra Content."
                  : "No lead channels yet — add them in Settings → Extra Content."
              }
              error={errors.channel}
            >
              <LeadChannelSelect
                value={form.channel}
                onValueChange={(value) =>
                  setForm((f) => ({ ...f, channel: value }))
                }
                valueField="name"
                placeholder="Select channel"
                aria-label="Channel"
                skip={!dialogOpen}
                triggerClassName="h-11 rounded-xl border-gray-200 bg-gray-50"
              />
            </SpendField>

            <SpendField
              label="Spend date"
              hint="Any day in the month — spend is grouped under that calendar month."
              error={errors.spentDate}
            >
              <Input
                type="date"
                value={form.spentDate}
                onChange={(e) =>
                  setForm((f) => ({ ...f, spentDate: e.target.value }))
                }
                className="h-11 rounded-xl border-gray-200 bg-gray-50 focus:bg-white"
              />
            </SpendField>

            <DialogFooter className="pt-2">
              <Button
                type="button"
                variant="ghost"
                onClick={() => handleDialogOpenChange(false)}
                className="rounded-xl"
              >
                Cancel
              </Button>
              <Button
                type="submit"
                disabled={creating}
                className="rounded-xl px-6"
              >
                Save spend
              </Button>
            </DialogFooter>
          </form>
        </DialogContent>
      </Dialog>

      <DeleteDialog
        open={Boolean(deleteId)}
        onOpenChange={(open) => !open && setDeleteId(null)}
        onConfirm={handleDelete}
        title="Remove spend entry?"
        description="This soft-deletes the spend row from the ledger."
      />
    </div>
  );
}
