"use client";

import * as React from "react";
import type { Milestone, PaymentStructure, PaymentTransaction } from "./types";
import { cn } from "@/lib/utils";
import { Trash2, Save, Loader2, CalendarDays } from "lucide-react";
import { format, parseISO } from "date-fns";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Calendar } from "@/components/ui/calendar";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { ReactSelect } from "@/components/ui/react-select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { toast } from "sonner";
import { ValidationError } from "yup";
import { assertPaymentStructureValidForSync } from "./payment-structure.validation";
import { buildPaymentStructurePatchBody } from "@/lib/to-view-payment-structure";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";

const PAYMENT_LEDGER_STATUS_OPTIONS = [
  { value: "paid", label: "Paid" },
  { value: "pending", label: "Pending" },
];

interface PaymentStructureProps {
  payment: PaymentStructure;
  onUpdate: (payment: PaymentStructure) => void;
  readOnly?: boolean;
  /**
   * When the rest of the schedule is read-only (e.g. no save target yet), still allow
   * toggling upfront status if the user can manage payment. Defaults to `readOnly`.
   */
  upfrontStatusReadOnly?: boolean;
  isSaving?: boolean;
  /** e.g. card padding when embedded in deal/customer pages */
  className?: string;
  autoPersistUpfrontStatus?: boolean;
  showAddCustomPaymentForm?: boolean;
  lockProjectValueField?: boolean;
  allowEditProjectValueWhenReadOnly?: boolean;
}

function parseAmount(s: string | undefined): number {
  if (s === undefined || s === null || s === "") return NaN;
  const n = parseFloat(String(s));
  return Number.isNaN(n) ? NaN : n;
}

/** Like {@link parseAmount} but allows comma-separated numbers from API strings. */
function parseAmountLoose(s: string | undefined | null): number {
  if (s === undefined || s === null || s === "") return NaN;
  const n = parseFloat(String(s).replace(/,/g, ""));
  return Number.isNaN(n) ? NaN : n;
}

function formatMoney(n: number): string {
  return n.toLocaleString(undefined, {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  });
}

/** Prefer created time; fall back to updated (API sometimes omits one). */
function formatLedgerDateTime(
  createdAt?: string | null,
  updatedAt?: string | null,
): string {
  const raw = createdAt ?? updatedAt;
  if (raw == null || String(raw).trim() === "") return "—";
  const d = new Date(raw);
  if (Number.isNaN(d.getTime())) return "—";
  return d.toLocaleString(undefined, {
    dateStyle: "medium",
    timeStyle: "short",
  });
}

function formatMilestoneDueTitle(
  ymd: string | null | undefined,
  emptyLabel: string,
): string {
  if (!ymd) return emptyLabel;
  try {
    return format(parseISO(ymd), "PPP");
  } catch {
    return String(ymd);
  }
}

function parseMilestoneDueSelected(
  ymd: string | null | undefined,
): Date | undefined {
  if (!ymd) return undefined;
  try {
    const d = parseISO(ymd);
    return Number.isNaN(d.getTime()) ? undefined : d;
  } catch {
    return undefined;
  }
}

function sumCustomAmountsTx(
  txs: PaymentTransaction[] | undefined,
  excludeId?: string,
): number {
  return (txs ?? [])
    .filter(
      (t) => t.kind === "custom" && (!excludeId || String(t.id) !== excludeId),
    )
    .reduce(
      (s, t) => s + (parseFloat(String(t.amount).replace(/,/g, "")) || 0),
      0,
    );
}

/**
 * When project value is not driving the UI, GET payment-structure may still send
 * `remainingAmount` that is **already** net of custom lines (see backend
 * `persistRemainingAmountForUpfrontAndCustoms`). In that case we must not subtract
 * custom amounts again from that figure.
 */
function apiUpfrontRemainingIsNetOfCustoms(
  mode: PaymentStructure["mode"],
  pvSafe: number,
  remFromApi: number,
): boolean {
  return (
    mode === "upfront" &&
    pvSafe <= 1e-9 &&
    !Number.isNaN(remFromApi) &&
    remFromApi >= 0
  );
}

/**
 * Headroom for custom lines: pool minus existing custom amounts (optional `excludeId`
 * when editing that row’s amount).
 */
function computeRemainingBalanceForCustomCap(
  p: PaymentStructure,
  excludeId?: string,
): number {
  const pv = parseAmount(p.projectVal);
  const pvSafe = Number.isNaN(pv) ? 0 : pv;
  const fixedParsed = parseAmount(p.fixedUpfront);
  const pctParsed = Math.min(100, Math.max(0, parseAmount(p.upfrontPct) || 0));
  let upfront: number;
  if (!Number.isNaN(fixedParsed)) {
    upfront = Math.min(Math.max(0, fixedParsed), pvSafe);
  } else {
    upfront = Math.min(pvSafe * (pctParsed / 100), pvSafe);
  }
  const remFromApi = parseAmountLoose(p.remainingAmount);
  const upFromApi = parseAmountLoose(p.upfrontAmount);
  const totalValFromApi = parseAmountLoose(p.totalValue);
  const displayUpfrontSummary =
    upfront > 1e-9
      ? upfront
      : !Number.isNaN(upFromApi) && upFromApi >= 0
        ? upFromApi
        : upfront;
  const effectiveUpfrontCapped =
    pvSafe > 1e-9
      ? Math.min(Math.max(0, displayUpfrontSummary), pvSafe)
      : Math.max(0, displayUpfrontSummary);
  const poolAfterUpfront =
    pvSafe > 1e-9 ? pvSafe - effectiveUpfrontCapped : pvSafe - upfront;
  const inferredTotalFromApi =
    (!Number.isNaN(remFromApi) && remFromApi >= 0 ? remFromApi : 0) +
    (!Number.isNaN(upFromApi) && upFromApi >= 0 ? upFromApi : 0);
  const displayProjectTotalSummary =
    pvSafe > 1e-9
      ? pvSafe
      : !Number.isNaN(totalValFromApi) && totalValFromApi > 1e-9
        ? totalValFromApi
        : inferredTotalFromApi > 1e-9
          ? inferredTotalFromApi
          : pvSafe;
  const displayRemainingAfterUpfront =
    pvSafe > 1e-9
      ? poolAfterUpfront
      : !Number.isNaN(remFromApi) && remFromApi >= 0
        ? remFromApi
        : Math.max(0, displayProjectTotalSummary - displayUpfrontSummary);
  const milestonePoolTarget =
    pvSafe > 1e-9 ? poolAfterUpfront : displayRemainingAfterUpfront;
  const paidMilestonesTotal = (p.milestones || []).reduce(
    (sum, ms) =>
      sum +
      (ms.status === "paid"
        ? parseFloat(String(ms.amount).replace(/,/g, "")) || 0
        : 0),
    0,
  );
  const msTotalAll = (p.milestones || []).reduce(
    (sum, ms) => sum + (parseFloat(String(ms.amount).replace(/,/g, "")) || 0),
    0,
  );
  const milestonePoolTargetResolved =
    p.mode === "milestone-based"
      ? milestonePoolTarget > 1e-9
        ? milestonePoolTarget
        : msTotalAll > 1e-9
          ? msTotalAll
          : !Number.isNaN(remFromApi) && remFromApi >= 0
            ? remFromApi + paidMilestonesTotal
            : milestonePoolTarget
      : milestonePoolTarget;
  const outstandingMilestonePool = Math.max(
    0,
    milestonePoolTargetResolved - paidMilestonesTotal,
  );
  if (
    p.mode !== "milestone-based" &&
    apiUpfrontRemainingIsNetOfCustoms(p.mode, pvSafe, remFromApi)
  ) {
    let bump = 0;
    if (excludeId) {
      const row = (p.transactions ?? []).find(
        (t) => t.kind === "custom" && String(t.id) === excludeId,
      );
      bump = parseFloat(String(row?.amount ?? "0").replace(/,/g, "")) || 0;
    }
    return Math.max(0, remFromApi + bump);
  }
  const base =
    p.mode === "milestone-based"
      ? outstandingMilestonePool
      : displayRemainingAfterUpfront;
  const customSum = sumCustomAmountsTx(p.transactions, excludeId);
  return Math.max(0, base - customSum);
}

const INPUT_ROW_H = "h-11";

function fixedUpfrontFieldIsUnset(
  rawFixed: string | number | undefined | null,
) {
  if (rawFixed === undefined || rawFixed === null) return true;
  const t = String(rawFixed).trim();
  if (t === "") return true;
  const n = parseAmount(t);
  return !Number.isNaN(n) && Math.abs(n) < 1e-9;
}

/**
 * When `fixedUpfront` is unset or zero, prefer a real `upfrontAmount` from the API
 * for display — except when the API mirrors the full project value (no real upfront).
 */
function applyDefaultFixedUpfrontFromPayment(
  payment: PaymentStructure,
): Pick<PaymentStructure, "milestones" | "fixedUpfront" | "upfrontStatus"> {
  const milestones = payment.milestones ?? [];
  const rawFixed = payment.fixedUpfront;
  let fixed: string;
  if (fixedUpfrontFieldIsUnset(rawFixed)) {
    const pvNum = parseAmount(String(payment.projectVal ?? ""));
    const pvSafe = Number.isNaN(pvNum) ? 0 : pvNum;
    const upAmt = parseAmountLoose(payment.upfrontAmount);
    const hasUp = !Number.isNaN(upAmt) && upAmt > 1e-9;
    const mirrorsFullProject =
      pvSafe > 1e-9 && Math.abs(upAmt - pvSafe) < 0.005;
    if (hasUp && !mirrorsFullProject) {
      fixed = Math.min(
        Math.max(0, upAmt),
        pvSafe > 1e-9 ? pvSafe : upAmt,
      ).toFixed(2);
    } else {
      fixed = "0.00";
    }
  } else {
    fixed = String(rawFixed).trim();
  }
  const upfrontStatus: PaymentStructure["upfrontStatus"] =
    payment.upfrontStatus === "paid" || payment.upfrontStatus === "pending"
      ? payment.upfrontStatus
      : "pending";
  return { milestones, fixedUpfront: fixed, upfrontStatus };
}

export function PaymentStructureView({
  payment,
  onUpdate,
  readOnly,
  upfrontStatusReadOnly,
  isSaving,
  className,
  autoPersistUpfrontStatus,
  showAddCustomPaymentForm = true,
  lockProjectValueField = false,
  allowEditProjectValueWhenReadOnly = false,
}: PaymentStructureProps) {
  const [localPayment, setLocalPayment] = React.useState<PaymentStructure>(
    () => ({
      ...payment,
      ...applyDefaultFixedUpfrontFromPayment(payment),
    }),
  );

  React.useEffect(() => {
    const defaults = applyDefaultFixedUpfrontFromPayment(payment);
    setLocalPayment({
      ...payment,
      ...defaults,
    });
  }, [payment]);

  const statusLocked = upfrontStatusReadOnly ?? readOnly;
  const projectValueFieldDisabled = Boolean(
    lockProjectValueField || (readOnly && !allowEditProjectValueWhenReadOnly),
  );

  const handleUpfrontStatusChange = async (
    val: NonNullable<PaymentStructure["upfrontStatus"]>,
  ) => {
    if (statusLocked) return;
    const prev = localPayment.upfrontStatus ?? "pending";
    const merged: PaymentStructure = { ...localPayment, upfrontStatus: val };
    setLocalPayment(merged);

    if (!autoPersistUpfrontStatus) {
      return;
    }

    try {
      await assertPaymentStructureValidForSync(merged);
    } catch (e) {
      setLocalPayment((p) => ({ ...p, upfrontStatus: prev }));
      if (e instanceof ValidationError) {
        const msg =
          e.errors?.filter(Boolean).join(" ") ||
          e.message ||
          "Fix milestone rows before saving.";
        toast.error(msg);
        return;
      }
      throw e;
    }

    try {
      await Promise.resolve(onUpdate(buildPaymentStructurePatchBody(merged)));
    } catch {
      setLocalPayment((p) => ({ ...p, upfrontStatus: prev }));
      toast.error("Failed to save upfront status");
    }
  };

  const handleLocalUpdate = (updates: Partial<PaymentStructure>) => {
    const keys = Object.keys(updates) as (keyof PaymentStructure)[];
    const onlyUpfrontStatus = keys.length === 1 && keys[0] === "upfrontStatus";
    if (onlyUpfrontStatus && !statusLocked) {
      setLocalPayment((prev) => ({ ...prev, ...updates }));
      return;
    }
    const projectValueKeys = new Set<keyof PaymentStructure>([
      "projectVal",
      "fixedUpfront",
      "upfrontPct",
    ]);
    const onlyProjectValueTuning =
      keys.length > 0 && keys.every((k) => projectValueKeys.has(k));
    if (
      onlyProjectValueTuning &&
      !lockProjectValueField &&
      (!readOnly || allowEditProjectValueWhenReadOnly)
    ) {
      setLocalPayment((prev) => ({ ...prev, ...updates }));
      return;
    }
    if (readOnly) return;
    setLocalPayment((prev) => ({ ...prev, ...updates }));
  };

  const [customDraft, setCustomDraft] = React.useState({
    amount: "",
    description: "",
    ledgerStatus: "paid" as "paid" | "pending",
    recordedOn: format(new Date(), "yyyy-MM-dd"),
  });
  const [customSubmitting, setCustomSubmitting] = React.useState(false);

  const pv = parseAmount(localPayment.projectVal);
  const pvSafe = Number.isNaN(pv) ? 0 : pv;
  const fixedParsed = parseAmount(localPayment.fixedUpfront);
  const pctParsed = Math.min(
    100,
    Math.max(0, parseAmount(localPayment.upfrontPct) || 0),
  );

  let upfront: number;
  if (!Number.isNaN(fixedParsed)) {
    upfront = Math.min(Math.max(0, fixedParsed), pvSafe);
  } else {
    upfront = Math.min(pvSafe * (pctParsed / 100), pvSafe);
  }

  /**
   * When `projectVal` is blank but the API persisted `upfrontAmount` / `remainingAmount`
   * (common on customer embed), still show sensible summary figures.
   */
  const remFromApi = parseAmountLoose(localPayment.remainingAmount);
  const upFromApi = parseAmountLoose(localPayment.upfrontAmount);
  const totalValFromApi = parseAmountLoose(localPayment.totalValue);
  const displayUpfrontSummary =
    upfront > 1e-9
      ? upfront
      : !Number.isNaN(upFromApi) && upFromApi >= 0
        ? upFromApi
        : upfront;
  const effectiveUpfrontCapped =
    pvSafe > 1e-9
      ? Math.min(Math.max(0, displayUpfrontSummary), pvSafe)
      : Math.max(0, displayUpfrontSummary);
  /** Post-upfront pool: milestone amounts must total this (not full project value). */
  const poolAfterUpfront =
    pvSafe > 1e-9 ? pvSafe - effectiveUpfrontCapped : pvSafe - upfront;
  const inferredTotalFromApi =
    (!Number.isNaN(remFromApi) && remFromApi >= 0 ? remFromApi : 0) +
    (!Number.isNaN(upFromApi) && upFromApi >= 0 ? upFromApi : 0);
  const displayProjectTotalSummary =
    pvSafe > 1e-9
      ? pvSafe
      : !Number.isNaN(totalValFromApi) && totalValFromApi > 1e-9
        ? totalValFromApi
        : inferredTotalFromApi > 1e-9
          ? inferredTotalFromApi
          : pvSafe;
  const displayRemainingAfterUpfront =
    pvSafe > 1e-9
      ? poolAfterUpfront
      : !Number.isNaN(remFromApi) && remFromApi >= 0
        ? remFromApi
        : Math.max(0, displayProjectTotalSummary - displayUpfrontSummary);

  /** Milestone pool target: match summary when project value is inferred from API. */
  const milestonePoolTarget =
    pvSafe > 1e-9 ? poolAfterUpfront : displayRemainingAfterUpfront;

  const paidMilestonesTotal = (localPayment.milestones || []).reduce(
    (sum, ms) =>
      sum +
      (ms.status === "paid"
        ? parseFloat(String(ms.amount).replace(/,/g, "")) || 0
        : 0),
    0,
  );
  const msTotal = (localPayment.milestones || []).reduce(
    (sum, ms) => sum + (parseFloat(String(ms.amount).replace(/,/g, "")) || 0),
    0,
  );
  const milestonePoolTargetResolved =
    localPayment.mode === "milestone-based"
      ? milestonePoolTarget > 1e-9
        ? milestonePoolTarget
        : msTotal > 1e-9
          ? msTotal
          : !Number.isNaN(remFromApi) && remFromApi >= 0
            ? remFromApi + paidMilestonesTotal
            : milestonePoolTarget
      : milestonePoolTarget;
  /** Cash still outstanding from the milestone pool (paid lines only reduce this). */
  const outstandingMilestonePool = Math.max(
    0,
    milestonePoolTargetResolved - paidMilestonesTotal,
  );

  /** Cash still owed — upfront: matches DB `(project−upfront)−custom`; milestone: pending milestone pool. */
  const customAmountsUsed = sumCustomAmountsTx(localPayment.transactions);
  const apiRemainingNetOfCustoms = apiUpfrontRemainingIsNetOfCustoms(
    localPayment.mode,
    pvSafe,
    remFromApi,
  );
  const remainingBalanceDisplay =
    localPayment.mode === "milestone-based"
      ? outstandingMilestonePool
      : apiRemainingNetOfCustoms
        ? Math.max(0, remFromApi)
        : Math.max(0, displayRemainingAfterUpfront - customAmountsUsed);
  /** Space left for more custom lines (same as remaining after customs in upfront mode). */
  const customPaymentHeadroom =
    computeRemainingBalanceForCustomCap(localPayment);
  const customLinesBudgetTotal =
    localPayment.mode === "milestone-based"
      ? outstandingMilestonePool
      : apiRemainingNetOfCustoms
        ? remFromApi + customAmountsUsed
        : displayRemainingAfterUpfront;

  const submitCustomPayment = async () => {
    if (readOnly) return;
    const n = parseFloat(String(customDraft.amount).replace(/,/g, ""));
    if (!Number.isFinite(n) || n <= 0) {
      toast.error("Enter a positive amount for the custom payment.");
      return;
    }
    const headroom = computeRemainingBalanceForCustomCap(localPayment);
    if (headroom < 1e-6) {
      toast.error(
        "No remaining balance left for custom lines. Lower other custom amounts or adjust the schedule.",
      );
      return;
    }
    const capped = Math.min(n, headroom);
    if (capped + 1e-6 < n) {
      toast.message(
        `Amount limited to ${formatMoney(capped)} so custom line totals stay within remaining balance (${formatMoney(customLinesBudgetTotal)}).`,
      );
    }
    const newId =
      typeof crypto !== "undefined" && crypto.randomUUID
        ? crypto.randomUUID()
        : `temp-${Date.now()}`;
    const row: PaymentTransaction = {
      id: newId,
      kind: "custom",
      sourceKey: `custom:${newId}`,
      amount: capped.toFixed(2),
      description: customDraft.description.trim() || "Custom payment",
      ledgerStatus: customDraft.ledgerStatus,
      recordedOn: customDraft.recordedOn.trim() || undefined,
    };
    const next = buildPaymentStructurePatchBody({
      ...localPayment,
      transactions: [...(localPayment.transactions ?? []), row],
    });
    setCustomSubmitting(true);
    try {
      await Promise.resolve(onUpdate(next));
      setLocalPayment(next);
      setCustomDraft({
        amount: "",
        description: "",
        ledgerStatus: "paid",
        recordedOn: format(new Date(), "yyyy-MM-dd"),
      });
      toast.success(
        "Custom payment saved. Ledger will refresh from the server.",
      );
    } catch {
      toast.error("Could not save custom payment.");
    } finally {
      setCustomSubmitting(false);
    }
  };

  const removeCustomPaymentRow = (id: string) => {
    if (readOnly) return;
    setLocalPayment((prev) => {
      const list = prev.transactions ?? [];
      return {
        ...prev,
        transactions: list.filter((t) => !(t.kind === "custom" && t.id === id)),
      };
    });
  };

  const patchCustomPaymentRow = (
    id: string,
    patch: Partial<
      Pick<
        PaymentTransaction,
        "ledgerStatus" | "amount" | "description" | "recordedOn"
      >
    >,
  ) => {
    if (readOnly) return;
    setLocalPayment((prev) => {
      const list = prev.transactions ?? [];
      const maxAmt = computeRemainingBalanceForCustomCap(prev, id);
      return {
        ...prev,
        transactions: list.map((t) => {
          if (t.kind !== "custom" || t.id !== id) return t;
          let next: PaymentTransaction = { ...t, ...patch };
          if (patch.amount !== undefined) {
            const num = parseFloat(String(patch.amount).replace(/,/g, ""));
            if (Number.isFinite(num) && num > maxAmt + 1e-9) {
              next = { ...next, amount: maxAmt.toFixed(2) };
              toast.message(
                maxAmt < 1e-6
                  ? "No headroom left under remaining balance for this line."
                  : `Amount capped at ${formatMoney(maxAmt)} (remaining balance).`,
              );
            }
          }
          return next;
        }),
      };
    });
  };

  const isExact = Math.abs(msTotal - milestonePoolTargetResolved) < 1;
  const isOver = msTotal > milestonePoolTargetResolved + 1;

  const onProjectValChange = (newPv: string) => {
    const nextPv = parseAmount(newPv);
    const nextPvSafe = Number.isNaN(nextPv) ? 0 : nextPv;
    const prevFixed = parseAmount(localPayment.fixedUpfront);
    if (!Number.isNaN(prevFixed)) {
      const c = Math.min(Math.max(0, prevFixed), nextPvSafe);
      const pctStr = nextPvSafe > 0 ? ((c / nextPvSafe) * 100).toFixed(2) : "0";
      handleLocalUpdate({
        projectVal: newPv,
        fixedUpfront: c.toFixed(2),
        upfrontPct: pctStr,
      });
    } else {
      handleLocalUpdate({
        projectVal: newPv,
        fixedUpfront: "0.00",
        upfrontPct: "0",
      });
    }
  };

  const onFixedUpfrontChange = (raw: string) => {
    const nextPvSafe = Number.isNaN(pv) ? 0 : pv;
    if (raw === "" || raw === ".") {
      handleLocalUpdate({ fixedUpfront: raw });
      return;
    }
    const n = parseAmount(raw);
    if (Number.isNaN(n)) {
      handleLocalUpdate({ fixedUpfront: raw });
      return;
    }
    const clamped = Math.min(Math.max(0, n), nextPvSafe);
    const pctStr =
      nextPvSafe > 0 ? ((clamped / nextPvSafe) * 100).toFixed(2) : "0";
    const displayFixed = clamped === n ? raw : clamped.toFixed(2);
    handleLocalUpdate({ fixedUpfront: displayFixed, upfrontPct: pctStr });
  };

  const upfrontRecorded =
    localPayment.upfrontStatus === "paid" && displayUpfrontSummary > 1e-9;
  const upfrontFieldLocked = Boolean(readOnly || upfrontRecorded);

  /** Ledger rows (API + local custom lines until next sync). */
  const persistedLedgerRows = React.useMemo(() => {
    const persisted = localPayment.transactions;
    if (!Array.isArray(persisted) || persisted.length === 0) return [];
    const sortKey = (t: PaymentTransaction) => {
      const ro = t.recordedOn?.trim();
      if (t.kind === "custom" && ro) return ro.slice(0, 10);
      return String(t.createdAt ?? t.updatedAt ?? "");
    };
    return [...persisted]
      .sort((a, b) => String(sortKey(b)).localeCompare(String(sortKey(a))))
      .map((t) => {
        const typeLabel =
          t.kind === "upfront"
            ? "Upfront"
            : t.kind === "custom"
              ? "Custom"
              : "Milestone";
        const ledger =
          t.kind === "custom"
            ? t.ledgerStatus === "pending"
              ? "pending"
              : "paid"
            : "paid";
        const dateLabel =
          t.kind === "custom" && t.recordedOn?.trim()
            ? formatMilestoneDueTitle(t.recordedOn, "—")
            : formatLedgerDateTime(t.createdAt, t.updatedAt);
        return {
          key: t.id,
          raw: t,
          type: typeLabel,
          description:
            (t.description && t.description.trim()) ||
            (t.kind === "upfront"
              ? "Upfront payment"
              : t.kind === "custom"
                ? "Custom payment"
                : "Milestone"),
          amount: formatMoney(parseFloat(String(t.amount)) || 0),
          status: ledger === "pending" ? "Pending" : "Paid",
          statusTone:
            ledger === "pending" ? ("pending" as const) : ("paid" as const),
          dateLabel,
        };
      });
  }, [localPayment.transactions]);

  const hasPersistedLedger = persistedLedgerRows.length > 0;

  return (
    <div
      className={cn(
        "flex w-full max-w-full flex-col gap-4 animate-in fade-in duration-500",
        className,
      )}
    >
      <div className="flex flex-col gap-3 rounded-xl border border-slate-200 bg-white px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
        <div>
          <p className="text-xs font-semibold text-slate-700">
            Upfront payment status
          </p>
          <p className="text-[11px] text-slate-500">
            Whether the upfront portion is paid—applies on Schedule and
            Transactions.
          </p>
        </div>
        <ReactSelect
          disabled={statusLocked || Boolean(isSaving)}
          value={localPayment.upfrontStatus ?? "pending"}
          onValueChange={handleUpfrontStatusChange}
          options={PAYMENT_LEDGER_STATUS_OPTIONS}
          triggerClassName="h-9 min-h-9 w-[140px] text-xs font-bold border-slate-200 sm:ml-auto"
        />
      </div>

      <Tabs defaultValue="schedule" className="w-full gap-4">
        <TabsList className="mb-1 h-auto min-h-9 w-full max-w-md flex-wrap justify-start gap-1 p-1 sm:w-fit">
          <TabsTrigger value="schedule" className="text-xs sm:text-sm">
            Schedule
          </TabsTrigger>
          <TabsTrigger
            value="transactions"
            className="text-xs sm:text-sm"
            title="Ledger: upfront, milestones, and optional custom payment lines"
          >
            Transactions
            <span className="ml-1 rounded-md bg-slate-200/80 px-1.5 py-0 text-[10px] font-semibold text-slate-700 tabular-nums">
              {persistedLedgerRows.length}
            </span>
          </TabsTrigger>
        </TabsList>

        <TabsContent value="schedule" className="mt-0 space-y-5 outline-none">
          {/* Project Overview Section */}
          <section className="w-full max-w-full space-y-4">
            <div className="grid w-full grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-5">
              <div className="flex min-w-0 flex-col gap-2">
                <label className="text-xs font-semibold text-slate-700">
                  Total deal value
                </label>
                <div
                  className={cn(
                    "flex w-full items-center gap-2 rounded-xl border border-slate-200 bg-white px-3 transition-shadow",
                    INPUT_ROW_H,
                    projectValueFieldDisabled
                      ? "cursor-not-allowed bg-slate-50"
                      : "focus-within:border-[#6C63FF] focus-within:ring-1 focus-within:ring-[#6C63FF]/25",
                  )}
                >
                  <Input
                    type="number"
                    className={cn(
                      "h-full min-h-0 flex-1 rounded-none border-0 bg-transparent p-0 shadow-none",
                      "text-sm outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
                      "focus-visible:border-transparent focus-visible:ring-0",
                      projectValueFieldDisabled && "cursor-not-allowed",
                    )}
                    value={localPayment.projectVal}
                    placeholder="0.00"
                    disabled={true}
                    // disabled={projectValueFieldDisabled}
                    onChange={(e) => onProjectValChange(e.target.value)}
                  />
                </div>
              </div>
              <div className="flex min-w-0 flex-col gap-2">
                <label className="text-xs font-semibold text-slate-700">
                  Upfront amount
                  {upfrontRecorded && !readOnly ? (
                    <span className="mt-0.5 block text-[10px] font-normal text-slate-500">
                      Locked while upfront is marked paid. Set status to Pending
                      to change the amount.
                    </span>
                  ) : null}
                </label>
                <div
                  className={cn(
                    "flex min-w-0 flex-1 items-center gap-2 rounded-xl border border-slate-200 px-3 transition-shadow",
                    INPUT_ROW_H,
                    upfrontFieldLocked
                      ? "cursor-not-allowed bg-slate-50"
                      : "bg-white focus-within:border-[#6C63FF] focus-within:ring-1 focus-within:ring-[#6C63FF]/25",
                  )}
                >
                  <Input
                    type="number"
                    min={0}
                    className={cn(
                      "h-full min-h-0 flex-1 rounded-none border-0 bg-transparent p-0 shadow-none",
                      "text-sm outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
                      "focus-visible:border-transparent focus-visible:ring-0",
                      upfrontFieldLocked && "cursor-not-allowed text-slate-600",
                    )}
                    placeholder="0.00"
                    value={localPayment.fixedUpfront ?? ""}
                    readOnly={readOnly}
                    disabled={!readOnly && upfrontRecorded}
                    onChange={(e) => onFixedUpfrontChange(e.target.value)}
                  />
                </div>
              </div>
            </div>

            <div className="grid w-full grid-cols-1 gap-3 rounded-xl border border-slate-100 bg-slate-50/90 p-4 sm:grid-cols-3 sm:gap-4 sm:p-5">
              <div>
                <p className="text-[10px] uppercase font-bold text-slate-500 tracking-wider">
                  Upfront Amount
                </p>
                <p className="text-lg font-semibold text-slate-900 tabular-nums">
                  {formatMoney(displayUpfrontSummary)}
                </p>
              </div>
              <div>
                <p className="text-[10px] uppercase font-bold text-slate-500 tracking-wider">
                  Remaining balance
                </p>
                <p className="text-lg font-semibold text-slate-900 tabular-nums">
                  {formatMoney(remainingBalanceDisplay)}
                </p>
                {localPayment.mode === "milestone-based" ? (
                  <p className="mt-1 text-[10px] text-slate-500">
                    After upfront (milestone pool):{" "}
                    <span className="font-semibold tabular-nums text-slate-700">
                      {formatMoney(milestonePoolTargetResolved)}
                    </span>
                  </p>
                ) : null}
              </div>
              <div>
                <p className="text-[10px] uppercase font-bold text-slate-500 tracking-wider">
                  Total Deal Value
                </p>
                <p className="text-lg font-semibold text-accent tabular-nums">
                  {formatMoney(displayProjectTotalSummary)}
                </p>
              </div>
            </div>

            {!readOnly && (
              <div className="flex w-full rounded-xl border border-slate-200 bg-slate-100 p-1">
                <button
                  type="button"
                  onClick={() => {
                    const v = Number.isNaN(pv) ? 0 : pv;
                    handleLocalUpdate({
                      mode: "upfront",
                      upfrontPct: "100",
                      fixedUpfront: v.toFixed(2),
                      upfrontStatus: localPayment.upfrontStatus ?? "pending",
                    });
                  }}
                  className={cn(
                    "h-9 flex-1 rounded-lg px-3 text-xs font-semibold transition-all sm:px-4",
                    localPayment.mode === "upfront"
                      ? "bg-white text-slate-900 shadow-sm"
                      : "text-slate-500 hover:text-slate-800",
                  )}
                >
                  Fixed Upfront
                </button>
                <button
                  type="button"
                  onClick={() => handleLocalUpdate({ mode: "milestone-based" })}
                  className={cn(
                    "h-9 flex-1 rounded-lg px-3 text-xs font-semibold transition-all sm:px-4",
                    localPayment.mode === "milestone-based"
                      ? "bg-white text-slate-900 shadow-sm"
                      : "text-slate-500 hover:text-slate-800",
                  )}
                >
                  Milestone Schedule
                </button>
              </div>
            )}
          </section>

          {/* Milestone Schedule Table */}
          {localPayment.mode === "milestone-based" && (
            <section className="space-y-4">
              <p className="text-[11px] leading-relaxed text-slate-500">
                Milestone amounts apply only to the balance after upfront (not
                the full project value). Their total should match &quot;After
                upfront (milestone pool)&quot; above.
              </p>
              <div className="rounded-xl border border-slate-200 bg-white overflow-hidden">
                <Table variant="transparent" className="border-collapse">
                  <TableHeader className="bg-slate-50/50 border-b border-slate-200">
                    <TableRow className="hover:bg-transparent cursor-default">
                      <TableHead className="w-3/5 px-4 h-12 text-[10px] font-bold text-slate-500 uppercase tracking-wider">
                        Milestone Description
                      </TableHead>
                      <TableHead
                        className="w-12 px-1 h-12 text-center text-[10px] font-bold text-slate-500 uppercase tracking-wider"
                        title="Target / billing date"
                      >
                        <span className="sr-only">Due date</span>
                        <CalendarDays
                          className="mx-auto size-3.5 text-slate-400"
                          aria-hidden
                        />
                      </TableHead>
                      <TableHead className="px-4 h-12 text-[10px] font-bold text-slate-500 uppercase tracking-wider text-right">
                        Amount
                      </TableHead>
                      <TableHead className="px-4 h-12 text-[10px] font-bold text-slate-500 uppercase tracking-wider text-center">
                        Status
                      </TableHead>
                      {!readOnly && <TableHead className="px-4 h-12 w-10"></TableHead>}
                    </TableRow>
                  </TableHeader>
                  <TableBody className="divide-y divide-slate-100">
                    {localPayment.milestones?.map((ms, idx) => (
                      <TableRow
                        key={idx}
                        className="hover:bg-slate-50/50 transition-colors cursor-default"
                      >
                        <TableCell className="px-4 py-2">
                          <input
                            type="text"
                            className={cn(
                              "w-full min-h-9 px-3 py-1.5 text-sm rounded-md border border-slate-200 outline-none transition-all",
                              "placeholder:text-slate-400",
                              readOnly
                                ? "bg-slate-50 text-slate-600 cursor-not-allowed"
                                : "bg-white focus:border-accent focus:ring-1 focus:ring-accent/20",
                            )}
                            placeholder="Enter description..."
                            value={ms.name}
                            readOnly={readOnly}
                            onChange={(e) => {
                              const newMs = [...localPayment.milestones];
                              newMs[idx] = {
                                ...newMs[idx],
                                name: e.target.value,
                              };
                              handleLocalUpdate({ milestones: newMs });
                            }}
                          />
                        </TableCell>
                        <TableCell className="px-1 py-2 text-center align-middle">
                          {readOnly ? (
                            <span
                              className="inline-flex h-9 w-9 items-center justify-center rounded-md text-slate-400"
                              title={formatMilestoneDueTitle(
                                ms.dueDate,
                                "No date set",
                              )}
                            >
                              <CalendarDays
                                className={cn(
                                  "size-4",
                                  ms.dueDate ? "text-accent" : "text-slate-300",
                                )}
                              />
                            </span>
                          ) : (
                            <Popover>
                              <PopoverTrigger asChild>
                                <Button
                                  type="button"
                                  variant={
                                    ms.dueDate
                                      ? "paymentCalendarIconDue"
                                      : "paymentCalendarIconEmpty"
                                  }
                                  title={formatMilestoneDueTitle(
                                    ms.dueDate,
                                    "Set due date",
                                  )}
                                >
                                  <CalendarDays className="size-4" />
                                </Button>
                              </PopoverTrigger>
                              <PopoverContent
                                className="w-auto p-0"
                                align="start"
                              >
                                <Calendar
                                  mode="single"
                                  captionLayout="dropdown"
                                  selected={parseMilestoneDueSelected(
                                    ms.dueDate,
                                  )}
                                  onSelect={(d) => {
                                    const newMs = [...localPayment.milestones];
                                    newMs[idx] = {
                                      ...newMs[idx],
                                      dueDate: d
                                        ? format(d, "yyyy-MM-dd")
                                        : null,
                                    };
                                    handleLocalUpdate({ milestones: newMs });
                                  }}
                                  initialFocus
                                />
                                {ms.dueDate ? (
                                  <div className="border-t border-border p-1">
                                    <Button
                                      type="button"
                                      variant="ghostFullRowMuted"
                                      onClick={() => {
                                        const newMs = [
                                          ...localPayment.milestones,
                                        ];
                                        newMs[idx] = {
                                          ...newMs[idx],
                                          dueDate: null,
                                        };
                                        handleLocalUpdate({
                                          milestones: newMs,
                                        });
                                      }}
                                    >
                                      Clear date
                                    </Button>
                                  </div>
                                ) : null}
                              </PopoverContent>
                            </Popover>
                          )}
                        </TableCell>
                        <TableCell className="px-4 py-2">
                          <input
                            type="number"
                            className={cn(
                              "w-full min-h-9 px-3 py-1.5 text-sm font-semibold text-right rounded-md border border-slate-200 outline-none transition-all",
                              "placeholder:text-slate-400",
                              readOnly
                                ? "bg-slate-50 text-slate-600 cursor-not-allowed"
                                : "bg-white focus:border-accent focus:ring-1 focus:ring-accent/20",
                            )}
                            placeholder="0.00"
                            value={ms.amount}
                            readOnly={readOnly}
                            onChange={(e) => {
                              const newMs = [...localPayment.milestones];
                              newMs[idx] = {
                                ...newMs[idx],
                                amount: e.target.value,
                              };
                              handleLocalUpdate({ milestones: newMs });
                            }}
                          />
                        </TableCell>
                        <TableCell className="px-4 py-2 text-center">
                          <ReactSelect
                            disabled={readOnly}
                            value={ms.status}
                            onValueChange={(val) => {
                              const newMs = [...localPayment.milestones];
                              newMs[idx] = {
                                ...newMs[idx],
                                status: val as Milestone["status"],
                              };
                              handleLocalUpdate({ milestones: newMs });
                            }}
                            options={PAYMENT_LEDGER_STATUS_OPTIONS}
                            triggerClassName="mx-auto h-7 min-h-7 w-24 text-[10px] font-bold border-slate-200"
                          />
                        </TableCell>
                        {!readOnly && (
                          <TableCell className="px-4 py-2 text-right">
                            <button
                              type="button"
                              onClick={() => {
                                const newMs = localPayment.milestones.filter(
                                  (_, i) => i !== idx,
                                );
                                handleLocalUpdate({ milestones: newMs });
                              }}
                              className="text-slate-300 hover:text-red transition-colors"
                            >
                              <Trash2 size={14} />
                            </button>
                          </TableCell>
                        )}
                      </TableRow>
                    ))}
                  </TableBody>
                </Table>
                {!readOnly && (
                  <button
                    type="button"
                    onClick={() => {
                      handleLocalUpdate({
                        milestones: [
                          ...(localPayment.milestones || []),
                          {
                            name: "",
                            amount: "",
                            status: "pending",
                            dueDate: null,
                            order: (localPayment.milestones || []).length,
                          },
                        ],
                      });
                    }}
                    className="w-full py-2 bg-slate-50 text-slate-600 text-xs font-medium border-t border-slate-200 hover:bg-slate-100 transition-colors"
                  >
                    + Add Billing Milestone
                  </button>
                )}
              </div>

              <div className="flex justify-between items-center px-4 py-3 bg-slate-50 border border-slate-200 rounded-md">
                <span className="text-xs font-bold text-slate-600">
                  Total Milestone Allocation
                </span>
                <div className="text-right">
                  <p
                    className={cn(
                      "text-sm font-bold",
                      isExact
                        ? "text-green"
                        : isOver
                          ? "text-red"
                          : "text-slate-900",
                    )}
                  >
                    {msTotal.toLocaleString(undefined, {
                      minimumFractionDigits: 2,
                    })}
                  </p>
                  <p className="text-[10px] text-slate-500 font-medium whitespace-nowrap">
                    target (after upfront):{" "}
                    {milestonePoolTargetResolved.toLocaleString(undefined, {
                      minimumFractionDigits: 2,
                    })}
                  </p>
                </div>
              </div>
            </section>
          )}

          {/* Footer Form Submission */}
          {!readOnly && (
            <div className="border-t border-slate-100 pt-4">
              <Button
                variant="paymentBlockAccent"
                onClick={async () => {
                  try {
                    await assertPaymentStructureValidForSync(localPayment);
                  } catch (e) {
                    if (e instanceof ValidationError) {
                      const msg =
                        e.errors?.filter(Boolean).join(" ") ||
                        e.message ||
                        "Fix milestone rows before syncing.";
                      toast.error(msg);
                      return;
                    }
                    throw e;
                  }
                  try {
                    await Promise.resolve(
                      onUpdate(buildPaymentStructurePatchBody(localPayment)),
                    );
                  } catch {
                    toast.error("Failed to sync payment schedule");
                  }
                }}
                disabled={isSaving}
              >
                {isSaving ? (
                  <Loader2 className="w-4 h-4 mr-2 animate-spin" />
                ) : (
                  <Save className="w-4 h-4 mr-2" />
                )}
                Sync Payment Schedule
              </Button>
            </div>
          )}
        </TabsContent>

        <TabsContent
          value="transactions"
          className="mt-0 min-w-0 space-y-4 outline-none"
        >
          {!readOnly && showAddCustomPaymentForm ? (
            <div className="rounded-xl border border-slate-200 bg-slate-50/50 p-4 space-y-3">
              <p className="text-xs font-semibold text-slate-800">
                Add custom payment
              </p>
              <p className="text-[11px] text-slate-500">
                Record additional payments or expected installments. Use{" "}
                <span className="font-medium text-slate-700">Pending</span> for
                amounts not yet received. Amounts cannot exceed the same{" "}
                <span className="font-medium text-slate-700">
                  remaining balance
                </span>{" "}
                as on the Schedule tab. Submit sends this line to the server and
                reloads transactions.
              </p>
              <p className="text-[11px] text-slate-600 tabular-nums">
                Custom lines:{" "}
                <span className="font-semibold text-slate-800">
                  {formatMoney(customAmountsUsed)}
                </span>{" "}
                of{" "}
                <span className="font-semibold text-slate-800">
                  {formatMoney(customLinesBudgetTotal)}
                </span>{" "}
                ·{" "}
                <span className="font-semibold text-accent">
                  {formatMoney(customPaymentHeadroom)}
                </span>{" "}
                headroom left
              </p>
              <div className="grid grid-cols-1 gap-3 sm:grid-cols-12 sm:items-end">
                <div className="sm:col-span-2 space-y-1">
                  <label className="text-[10px] font-bold uppercase text-slate-500">
                    Date
                  </label>
                  <Popover>
                    <PopoverTrigger asChild>
                      <Button type="button" variant="outlineMilestoneDateBtn">
                        <CalendarDays className="mr-1.5 size-3.5 shrink-0 text-slate-400" />
                        {formatMilestoneDueTitle(
                          customDraft.recordedOn,
                          "Pick date",
                        )}
                      </Button>
                    </PopoverTrigger>
                    <PopoverContent className="w-auto p-0" align="start">
                      <Calendar
                        mode="single"
                        captionLayout="dropdown"
                        selected={parseMilestoneDueSelected(
                          customDraft.recordedOn,
                        )}
                        onSelect={(d) =>
                          setCustomDraft((x) => ({
                            ...x,
                            recordedOn: d
                              ? format(d, "yyyy-MM-dd")
                              : format(new Date(), "yyyy-MM-dd"),
                          }))
                        }
                        initialFocus
                      />
                    </PopoverContent>
                  </Popover>
                </div>
                <div className="sm:col-span-2 space-y-1">
                  <label className="text-[10px] font-bold uppercase text-slate-500">
                    Amount
                  </label>
                  <Input
                    type="number"
                    min={0.01}
                    max={
                      customPaymentHeadroom > 0
                        ? customPaymentHeadroom
                        : undefined
                    }
                    step="0.01"
                    className="h-9 rounded-lg border-slate-200 px-2 text-sm focus:border-[#6C63FF]"
                    placeholder="0.00"
                    value={customDraft.amount}
                    onChange={(e) =>
                      setCustomDraft((d) => ({ ...d, amount: e.target.value }))
                    }
                  />
                </div>
                <div className="sm:col-span-4 space-y-1">
                  <label className="text-[10px] font-bold uppercase text-slate-500">
                    Description
                  </label>
                  <input
                    type="text"
                    className="h-9 w-full rounded-lg border border-slate-200 bg-white px-2 text-sm outline-none focus:border-[#6C63FF]"
                    placeholder="e.g. Retainer, wire #2"
                    value={customDraft.description}
                    onChange={(e) =>
                      setCustomDraft((d) => ({
                        ...d,
                        description: e.target.value,
                      }))
                    }
                  />
                </div>
                <div className="sm:col-span-2 space-y-1">
                  <label className="text-[10px] font-bold uppercase text-slate-500">
                    Status
                  </label>
                  <ReactSelect
                    value={customDraft.ledgerStatus}
                    onValueChange={(v) =>
                      setCustomDraft((d) => ({
                        ...d,
                        ledgerStatus: v as "paid" | "pending",
                      }))
                    }
                    options={PAYMENT_LEDGER_STATUS_OPTIONS}
                    triggerClassName="h-9 min-h-9 text-xs border-slate-200 bg-white"
                  />
                </div>
                <div className="sm:col-span-2">
                  <Button
                    type="button"
                    variant="paymentCustomRowSubmit"
                    disabled={readOnly || isSaving || customSubmitting}
                    onClick={() => void submitCustomPayment()}
                  >
                    {customSubmitting || isSaving ? (
                      <>
                        <Loader2 className="mr-1.5 size-3.5 animate-spin" />
                        Saving…
                      </>
                    ) : (
                      "Submit"
                    )}
                  </Button>
                </div>
              </div>
            </div>
          ) : null}

          <div className="min-w-0 overflow-x-auto rounded-xl border border-slate-200 bg-white">
          <Table variant="transparent" className="border-collapse text-sm min-w-[640px]">
            <TableHeader className="border-b border-slate-200 bg-slate-50/50">
              <TableRow className="hover:bg-transparent cursor-default">
                <TableHead className="px-4 h-12 text-[10px] font-bold uppercase tracking-wider text-slate-500">
                  Date
                </TableHead>
                <TableHead className="px-4 h-12 text-[10px] font-bold uppercase tracking-wider text-slate-500">
                  Type
                </TableHead>
                <TableHead className="px-4 h-12 text-[10px] font-bold uppercase tracking-wider text-slate-500">
                  Description
                </TableHead>
                <TableHead className="px-4 h-12 text-right text-[10px] font-bold uppercase tracking-wider text-slate-500">
                  Amount
                </TableHead>
                <TableHead className="px-4 h-12 text-center text-[10px] font-bold uppercase tracking-wider text-slate-500">
                  Status
                </TableHead>
                {!readOnly ? (
                  <TableHead className="px-4 h-12 text-right text-[10px] font-bold uppercase tracking-wider text-slate-500 w-[100px]">
                    Actions
                  </TableHead>
                ) : null}
              </TableRow>
            </TableHeader>
            <TableBody className="divide-y divide-slate-100">
              {persistedLedgerRows.length === 0 ? (
                <TableRow className="hover:bg-transparent cursor-default">
                  <TableCell
                    colSpan={readOnly ? 5 : 6}
                    className="px-4 py-10 text-center text-[13px] leading-relaxed text-slate-500 whitespace-normal"
                  >
                    No recorded payments yet. On the Schedule tab, set{" "}
                    <span className="font-medium text-slate-700">
                      Upfront payment status
                    </span>{" "}
                    to Paid and sync—paid milestones sync the same way. Use
                    Submit above to add a custom payment line without syncing
                    the whole schedule.
                  </TableCell>
                </TableRow>
              ) : (
                persistedLedgerRows.map((row) => (
                  <TableRow
                    key={row.key}
                    className="bg-white hover:bg-slate-50/50 transition-colors cursor-default"
                  >
                    <TableCell className="whitespace-nowrap px-4 py-3 text-xs text-slate-600">
                      {row.raw.kind === "custom" && !readOnly ? (
                        <Popover>
                          <PopoverTrigger asChild>
                            <Button
                              type="button"
                              variant="ghostCustomPaymentDate"
                            >
                              <CalendarDays className="size-3.5 shrink-0 text-slate-400" />
                              {formatMilestoneDueTitle(
                                row.raw.recordedOn,
                                "Set date",
                              )}
                            </Button>
                          </PopoverTrigger>
                          <PopoverContent className="w-auto p-0" align="start">
                            <Calendar
                              mode="single"
                              captionLayout="dropdown"
                              selected={parseMilestoneDueSelected(
                                row.raw.recordedOn,
                              )}
                              onSelect={(d) =>
                                patchCustomPaymentRow(row.raw.id, {
                                  recordedOn: d
                                    ? format(d, "yyyy-MM-dd")
                                    : undefined,
                                })
                              }
                              initialFocus
                            />
                            {row.raw.recordedOn ? (
                              <div className="border-t border-border p-1">
                                <Button
                                  type="button"
                                  variant="ghostFullRowMuted"
                                  onClick={() =>
                                    patchCustomPaymentRow(row.raw.id, {
                                      recordedOn: undefined,
                                    })
                                  }
                                >
                                  Clear date
                                </Button>
                              </div>
                            ) : null}
                          </PopoverContent>
                        </Popover>
                      ) : (
                        row.dateLabel
                      )}
                    </TableCell>
                    <TableCell className="px-4 py-3 font-medium text-slate-800">
                      {row.type}
                    </TableCell>
                    <TableCell className="px-4 py-3 text-slate-700">
                      {row.raw.kind === "custom" && !readOnly ? (
                        <input
                          type="text"
                          className="w-full min-w-0 rounded-md border border-slate-200 bg-white px-2 py-1 text-xs outline-none focus:border-[#6C63FF]"
                          value={row.raw.description ?? ""}
                          onChange={(e) =>
                            patchCustomPaymentRow(row.raw.id, {
                              description: e.target.value,
                            })
                          }
                        />
                      ) : (
                        row.description
                      )}
                    </TableCell>
                    <TableCell className="px-4 py-3 text-right font-semibold tabular-nums text-slate-900">
                      {row.raw.kind === "custom" && !readOnly ? (
                        <input
                          type="number"
                          min={0.01}
                          max={Math.max(
                            0.01,
                            computeRemainingBalanceForCustomCap(
                              localPayment,
                              row.raw.id,
                            ),
                          )}
                          step="0.01"
                          className="w-full max-w-[120px] ml-auto rounded-md border border-slate-200 bg-white px-2 py-1 text-xs text-right outline-none focus:border-[#6C63FF]"
                          value={row.raw.amount}
                          onChange={(e) =>
                            patchCustomPaymentRow(row.raw.id, {
                              amount: e.target.value,
                            })
                          }
                        />
                      ) : (
                        row.amount
                      )}
                    </TableCell>
                    <TableCell className="px-4 py-3 text-center">
                      {row.raw.kind === "custom" && !readOnly ? (
                        <ReactSelect
                          value={row.raw.ledgerStatus ?? "paid"}
                          onValueChange={(v) =>
                            patchCustomPaymentRow(row.raw.id, {
                              ledgerStatus: v as "paid" | "pending",
                            })
                          }
                          options={PAYMENT_LEDGER_STATUS_OPTIONS}
                          triggerClassName="mx-auto h-8 min-h-8 w-[110px] text-[10px] font-bold border-slate-200"
                        />
                      ) : (
                        <span
                          className={cn(
                            "inline-flex rounded-md px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide",
                            row.statusTone === "pending"
                              ? "bg-amber-50 text-amber-900"
                              : "bg-emerald-50 text-emerald-800",
                          )}
                        >
                          {row.status}
                        </span>
                      )}
                    </TableCell>
                    {!readOnly ? (
                      <TableCell className="px-4 py-3 text-right">
                        {row.raw.kind === "custom" ? (
                          <Button
                            type="button"
                            variant="ghostRowRemoveSlate"
                            onClick={() => removeCustomPaymentRow(row.raw.id)}
                            aria-label="Remove custom payment"
                          >
                            <Trash2 className="h-4 w-4" />
                          </Button>
                        ) : (
                          <span className="text-[10px] text-slate-300">—</span>
                        )}
                      </TableCell>
                    ) : null}
                  </TableRow>
                ))
              )}
            </TableBody>
          </Table>
          </div>
          {persistedLedgerRows.length > 0 ? (
            <p className="text-[11px] text-slate-500">
              Rows load from the server after each successful sync. Custom lines
              are stored as payment transactions with paid or pending status.
            </p>
          ) : null}
        </TabsContent>
      </Tabs>
    </div>
  );
}
