"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import {
  ArrowLeft,
  ArrowRight,
  ArrowUpRight,
  Banknote,
  Building2,
  Check,
  CheckCircle2,
  ChevronDown,
  Plus,
  Search,
  Send as SendIcon,
  Smartphone,
  Sparkles,
  X,
} from "lucide-react";
import ReactSelect, { type DropdownIndicatorProps, type MenuListProps, type InputProps, components } from "react-select";
import { Container } from "@/components/common/Container";
import { Card, CardContent } from "@/components/common/Card";
import { Button, ButtonLink } from "@/components/common/Button";
import { Stepper } from "@/components/app/Stepper";
import { EmptyState } from "@/components/app/EmptyState";
import { sendNewBeneficiaryHref, transactionDetailHref } from "@/constants/routes";
import { COUNTRIES } from "@/data/countries";
import { cn, formatNumber } from "@/lib/utils";
import { useSendIndex, useSendBeneficiary, useReceiptPayment } from "@/hooks/queries/useSendRemittance";
import type { ApiBeneficiary } from "@/hooks/queries/useSendRemittance";
import { sendRemittanceApi } from "@/api/sendRemittance.api";
import type { ApiCoupon, ApiNewUserCoupon } from "@/api/sendRemittance.api";
import { useTranslation } from "@/hooks/useTranslation";
import { isApiError } from "@/lib/apiError";
import { useKycGate } from "@/hooks/useKycGate";
import { KycGate } from "@/components/app/KycGate";
import { useControlledSelectMenu } from "@/hooks/useControlledSelectMenu";
import type { TransferMethod } from "@/types";

interface SendResult {
  send: number;
  receive: number;
  rate: number;
  transferFee: number;
  totalToPay: number;
  discount: number;
}

const TX_TYPE_SLUG: Record<TransferMethod, string> = {
  bank: "bank-transfer",
  cash: "cash-pickup",
  mobile: "mobile_money",
};

// A page reload remounts SendPage from scratch — every amount/recipient/payment
// field resets to its default, so landing on step 2+ with blank fields would
// show a broken partial flow. Navigation Timing tells an actual reload (F5,
// accidental refresh) apart from a real navigation to this URL (clicking
// through the flow, or a payment gateway redirecting back) — only reload
// forces back to step 1. Pure read, safe to call during render.
function isStaleReloadStep(): boolean {
  if (typeof window === "undefined") return false;
  const p = new URLSearchParams(window.location.search).get("step");
  if (p === null) return false;
  const requestedStep = Math.min(3, Math.max(0, parseInt(p, 10) - 1));
  if (requestedStep <= 0) return false;
  const [navEntry] = performance.getEntriesByType("navigation") as PerformanceNavigationTiming[];
  return navEntry?.type === "reload";
}

const SLUG_METHOD: Record<string, TransferMethod> = {
  "bank-transfer": "bank",
  "cash-pickup": "cash",
  mobile_money: "mobile",
};

const SLUG_ICON: Record<string, React.ComponentType<{ className?: string }>> = {
  "bank-transfer": Building2,
  "cash-pickup": Banknote,
  mobile_money: Smartphone,
};

const FALLBACK_TX_TYPES = [
  { slug: "bank-transfer", title: "Bank Transfer" },
  { slug: "cash-pickup", title: "Cash Pickup" },
  { slug: "mobile_money", title: "Mobile Money" },
];

// Coerce the personal new-user coupon → ApiCoupon.
// Price always from the personal coupon; borrow coupon_type from platform entry if name matches.
// max_limit on the personal coupon is a usage count (same as remaining), not a monetary cap — set "0" to skip cap logic.
function toApiCoupon(c: ApiNewUserCoupon, indexCoupons: ApiCoupon[]): ApiCoupon {
  const found = indexCoupons.find(
    (ic) => ic.name.toLowerCase() === c.name.toLowerCase(),
  );
  return {
    id: c.id,
    coupon_type: found?.coupon_type ?? c.coupon_type ?? "fixed",
    name: c.name,
    price: String(c.price ?? 0),
    max_limit: "0",
    remaining: c.remaining ?? 0,
  };
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function CurrencyMenuList(props: MenuListProps<any>) {
  return (
    <components.MenuList {...props}>
      <div className="flex items-center gap-2.5 border-b border-slate-200 px-4 dark:border-white/[0.08]">
        <Search className="h-4 w-4 shrink-0 text-slate-400" />
        <input
          autoFocus
          value={props.selectProps.inputValue}
          onChange={(e) =>
            props.selectProps.onInputChange(e.target.value, { action: "input-change", prevInputValue: props.selectProps.inputValue })
          }
          onMouseDown={(e) => e.stopPropagation()}
          onTouchEnd={(e) => e.stopPropagation()}
          placeholder="Search..."
          style={{ outline: "none", boxShadow: "none" }}
          className="flex-1 bg-transparent py-3 text-sm text-slate-900 placeholder:text-slate-400 dark:text-white"
        />
      </div>
      {props.children}
    </components.MenuList>
  );
}

// isSearchable={false} renders DummyInput (ignores components.Input), so we use
// isSearchable={true} + this override: always hidden in control, onBlur stripped
// so autoFocus on the menu's search input doesn't trigger the blur→close chain.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function CurrencyInput(props: InputProps<any>) {
  const { onBlur: _discarded, ...rest } = props;
  return <components.Input {...rest} isHidden={true} />;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function TxDropdownIndicator(props: DropdownIndicatorProps<any>) {
  return (
    <components.DropdownIndicator {...props}>
      <ChevronDown
        className={cn(
          "h-3.5 w-3.5 text-slate-400 transition-transform duration-200",
          props.selectProps.menuIsOpen && "rotate-180",
        )}
      />
    </components.DropdownIndicator>
  );
}

const STEP2_SELECT_CLS = {
  control: ({ menuIsOpen }: { menuIsOpen: boolean }) =>
    cn(
      "h-11 cursor-pointer border border-slate-200 bg-white px-3 transition-[border-radius] duration-150 dark:border-white/[0.08] dark:bg-white/[0.04]",
      menuIsOpen ? "rounded-t-2xl rounded-b-none" : "rounded-2xl",
    ),
  menu: () =>
    "select-menu-portal overflow-hidden rounded-b-2xl border border-t-0 border-slate-200 bg-white shadow-lg dark:border-white/[0.08] dark:bg-slate-900",
  option: ({ isFocused, isSelected }: { isFocused: boolean; isSelected: boolean }) =>
    cn(
      "cursor-pointer px-4 py-2.5 text-sm transition-colors",
      isSelected
        ? "bg-brand-50 text-brand-700 font-medium dark:bg-brand-500/15 dark:text-brand-300"
        : isFocused
          ? "bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-white"
          : "text-slate-700 dark:text-slate-200",
    ),
  singleValue: () => "text-sm text-slate-900 dark:text-white",
  placeholder: () => "text-sm text-slate-400",
  indicatorsContainer: () => "text-slate-400",
  input: () => "text-slate-900 dark:text-white",
  menuList: () => "max-h-56 overflow-y-auto [&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-slate-300 dark:[&::-webkit-scrollbar-thumb]:bg-slate-600",
};

export default function SendPage() {
  const { t } = useTranslation();
  const router = useRouter();
  const kycGateStatus = useKycGate();

  const [step, setStep] = useState(() => {
    if (typeof window === "undefined") return 0;
    if (isStaleReloadStep()) return 0;
    const sp = new URLSearchParams(window.location.search);
    const p = sp.get("step");
    // URL ?step is 1-indexed (step=2 → state index 1)
    return p !== null ? Math.min(3, Math.max(0, parseInt(p, 10) - 1)) : 0;
  });

  // history.replaceState is patched by Next's router — mutating it during
  // render (as the initializer above would) trips "setState while rendering
  // a different component." Do the actual URL cleanup here instead, once
  // mounted.
  useEffect(() => {
    if (isStaleReloadStep()) {
      window.history.replaceState(null, "", "/send");
    }
  }, []);

  const navigateToStep = useCallback(
    (n: number) => {
      setStep(n);
      router.replace(n === 0 ? "/send" : `/send?step=${n + 1}`);
    },
    [router],
  );

  const [submitting, setSubmitting] = useState(false);
  const [stepLoading, setStepLoading] = useState(false);
  const [identifier, setIdentifier] = useState("");
  const [successId, setSuccessId] = useState<string | null>(null);
  const [successRef, setSuccessRef] = useState<string>("");

  const [sendAmount, setSendAmount] = useState<string>("500");
  const [from, setFrom] = useState("USD");
  const [to, setTo] = useState("BDT");
  const [method, setMethod] = useState<TransferMethod>("bank");

  const [recipientId, setRecipientId] = useState("");
  const [recipientSearch, setRecipientSearch] = useState("");

  const [sendingPurpose, setSendingPurpose] = useState("");
  const [sourceOfFund, setSourceOfFund] = useState("");
  const [remarks, setRemarks] = useState("");
  const [paymentGateway, setPaymentGateway] = useState("");
  const [terms, setTerms] = useState(false);
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [couponInput, setCouponInput] = useState("");
  const [appliedCoupon, setAppliedCoupon] = useState<ApiCoupon | null>(null);
  const [couponError, setCouponError] = useState("");

  const senderRef = useRef<HTMLDivElement>(null);
  const senderMenu = useControlledSelectMenu(senderRef, "select-menu-portal");
  const receiverRef = useRef<HTMLDivElement>(null);
  const receiverMenu = useControlledSelectMenu(receiverRef, "select-menu-portal");
  const gatewayRef = useRef<HTMLDivElement>(null);
  const gatewayMenu = useControlledSelectMenu(gatewayRef, "select-menu-portal");

  const { data: indexData } = useSendIndex(TX_TYPE_SLUG[method]);
  const { data: beneficiaryData, isLoading: beneficiaryLoading, isError: beneficiaryErrored } = useSendBeneficiary(identifier);
  const { data: receiptPaymentData, isLoading: receiptPaymentLoading } = useReceiptPayment();
  const senderCurrencies = useMemo(() => indexData?.sender_currency ?? [], [indexData]);
  const receiverCurrencies = useMemo(() => indexData?.receiver_currency ?? [], [indexData]);
  const txTypes = useMemo(() => indexData?.transaction_type ?? [], [indexData]);
  const imagePaths = indexData?.image_paths;
  const coupons = useMemo(() => indexData?.coupons ?? [], [indexData]);
  // Personal coupon is valid when present and has remaining uses.
  const newUserCoupon = indexData?.new_user_coupon;
  const myCoupon: ApiNewUserCoupon | null =
    newUserCoupon != null && (newUserCoupon.remaining ?? 0) > 0 ? newUserCoupon : null;

  const [activePromoIndex, setActivePromoIndex] = useState(0);

  const availablePromos = useMemo(() => {
    const list: Array<{ id: string; name: string; discountDesc: string; apiCoupon: ApiCoupon }> = [];
    if (myCoupon?.name && (myCoupon.remaining ?? 0) > 0) {
      const apiC = toApiCoupon(myCoupon, coupons);
      list.push({
        id: `personal-${myCoupon.id}`,
        name: myCoupon.name,
        discountDesc: `${parseFloat(apiC.price)}${apiC.coupon_type.toLowerCase().includes("percent") ? "% OFF" : " Flat OFF"}`,
        apiCoupon: apiC,
      });
    }
    coupons.forEach((c) => {
      if (c.remaining > 0 && !list.some((item) => item.name.toLowerCase() === c.name.toLowerCase())) {
        list.push({
          id: `platform-${c.id}`,
          name: c.name,
          discountDesc: `${parseFloat(c.price)}${c.coupon_type.toLowerCase().includes("percent") ? "% OFF" : " Flat OFF"}`,
          apiCoupon: c,
        });
      }
    });
    return list;
  }, [myCoupon, coupons]);

  useEffect(() => {
    if (availablePromos.length <= 1 || appliedCoupon) return;
    const timer = setInterval(() => {
      setActivePromoIndex((prev) => (prev + 1) % availablePromos.length);
    }, 4500);
    return () => clearInterval(timer);
  }, [availablePromos.length, appliedCoupon]);

  const currentPromo = availablePromos[activePromoIndex % availablePromos.length] ?? availablePromos[0] ?? null;

  const purposeOptions = useMemo(
    () => (receiptPaymentData?.sending_purposes ?? []).map((p) => ({ value: String(p.id), label: p.name })),
    [receiptPaymentData],
  );
  const sourceOptions = useMemo(
    () => (receiptPaymentData?.source_of_funds ?? []).map((s) => ({ value: String(s.id), label: s.name })),
    [receiptPaymentData],
  );
  const gatewayOptions = useMemo(
    () => (receiptPaymentData?.payment_gateway ?? []).map((g) => ({ value: String(g.id), label: g.name })),
    [receiptPaymentData],
  );
  const selectedGateway = useMemo(
    () => receiptPaymentData?.payment_gateway.find((g) => String(g.id) === paymentGateway) ?? null,
    [receiptPaymentData, paymentGateway],
  );

  type CurrencyOpt = { value: string; label: string; flag: string };
  const buildFlagUrl = (flag: string): string =>
    imagePaths ? `${imagePaths.base_url}${imagePaths.path_location}/${flag}` : "";
  const senderOptions: CurrencyOpt[] = senderCurrencies.map(c => ({
    value: c.code, label: c.name, flag: buildFlagUrl(c.flag),
  }));
  const receiverOptions: CurrencyOpt[] = receiverCurrencies.map(c => ({
    value: c.code, label: c.name, flag: buildFlagUrl(c.flag),
  }));

  const [senderSearch, setSenderSearch] = useState("");
  const [receiverSearch, setReceiverSearch] = useState("");
  const [gatewaySearch, setGatewaySearch] = useState("");

  // Keep selection valid when currency lists change (e.g. on method switch).
  // Restore persisted state after mount (SSR-safe — window not available on server)
  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const bidParam = params.get("bid");
    const stepParam = params.get("step");
    const resumedStep = stepParam !== null ? Math.min(3, Math.max(0, parseInt(stepParam, 10) - 1)) : 0;

    // The cached identifier is single-use per attempt — minted by pressing Continue
    // on step 0. Only trust it when the URL says we're actually resuming past step 0
    // (e.g. a reload mid-flow); a fresh landing on step 0 means a new transfer, and
    // the old value may be expired/consumed server-side (throws on the beneficiary
    // fetch, which fires as soon as `identifier` is set).
    if (resumedStep > 0) {
      const savedId = localStorage.getItem("send_identifier");
      if (savedId) setIdentifier(savedId);
    } else {
      localStorage.removeItem("send_identifier");
    }

    // Only strip bid (one-time use). Keep step in URL for tracking.
    if (params.has("bid")) {
      const clean = new URL(window.location.href);
      clean.searchParams.delete("bid");
      window.history.replaceState({}, "", clean.toString());
    }
    if (bidParam) setRecipientId(bidParam);
    // Apply step from URL — covers both fresh mount and router-cache restores
    if (stepParam !== null) {
      setStep(resumedStep);
    }
  }, []);

  // Defense in depth: even a legitimately-resumed identifier (or one minted moments
  // ago at step 0) can turn out invalid/expired server-side — e.g. the backend's TTL
  // elapses while the user idles on step 2/3. Recover instead of leaving them stuck
  // on a step whose data will never load.
  useEffect(() => {
    if (!beneficiaryErrored || !identifier) return;
    setIdentifier("");
    localStorage.removeItem("send_identifier");
    setErrors({ amount: t("app.send.errors.sessionExpired") });
    navigateToStep(0);
  }, [beneficiaryErrored, identifier, navigateToStep, t]);

  useEffect(() => {
    if (senderCurrencies.length && !senderCurrencies.some(c => c.code === from)) {
      setFrom(senderCurrencies[0].code);
    }
  }, [senderCurrencies, from]);

  useEffect(() => {
    if (receiverCurrencies.length && !receiverCurrencies.some(c => c.code === to)) {
      setTo(receiverCurrencies[0].code);
    }
  }, [receiverCurrencies, to]);

  // Drop the applied coupon if it's no longer in the (possibly refreshed) list.
  useEffect(() => {
    if (appliedCoupon && !coupons.some(c => c.id === appliedCoupon.id)) {
      setAppliedCoupon(null);
      setCouponInput("");
      setCouponError("");
    }
  }, [coupons, appliedCoupon]);

  const stepDefs = useMemo(
    () => [
      { id: 1, label: t("app.send.step1") },
      { id: 2, label: t("app.send.step2") },
      { id: 3, label: t("app.send.step3") },
      { id: 4, label: t("app.send.step4") },
    ],
    [t],
  );

  const result = useMemo((): SendResult => {
    const amt = parseFloat(sendAmount) || 0;
    const zero: SendResult = { send: 0, receive: 0, rate: 0, transferFee: 0, totalToPay: 0, discount: 0 };
    if (amt <= 0) return zero;
    const senderCur = senderCurrencies.find(c => c.code === from);
    const receiverCur = receiverCurrencies.find(c => c.code === to);
    const slug = TX_TYPE_SLUG[method];
    const txType = txTypes.find(t => t.slug === slug) ?? txTypes[0];
    if (!senderCur || !receiverCur || !txType) return zero;
    // Both rates are relative to USD. 1 senderCur = receiverRate / senderRate receiverCur.
    const exchangeRate = parseFloat(receiverCur.rate) / parseFloat(senderCur.rate);
    // Tiered fees: match the send amount to an interval, fall back to tx-type defaults.
    const interval = txType.intervals.find(
      i => amt >= parseFloat(i.min_limit) && amt <= parseFloat(i.max_limit),
    );
    const fixedFee = parseFloat(interval?.fixed ?? txType.fixed_charge);
    const pctFee = parseFloat(interval?.percent ?? txType.percent_charge);
    const transferFee = +(fixedFee + amt * pctFee / 100).toFixed(2);
    const receive = +(amt * exchangeRate).toFixed(2);
    // Coupon discount: percentage types scale with the amount, otherwise flat.
    // max_limit on this API is usage count, not a monetary cap — not applied here.
    let discount = 0;
    if (appliedCoupon) {
      const price = parseFloat(appliedCoupon.price) || 0;
      discount = +(appliedCoupon.coupon_type.toLowerCase().includes("percent")
        ? (amt * price) / 100
        : price
      ).toFixed(2);
    }
    const totalToPay = +(amt + transferFee - discount).toFixed(2);
    return { send: amt, receive, rate: +exchangeRate.toFixed(4), transferFee, totalToPay, discount };
  }, [sendAmount, from, to, method, senderCurrencies, receiverCurrencies, txTypes, appliedCoupon]);

  const beneficiaries: ApiBeneficiary[] = useMemo(
    () => beneficiaryData?.beneficiaries ?? [],
    [beneficiaryData],
  );

  const filtered = useMemo(() => {
    const term = recipientSearch.trim().toLowerCase();
    if (!term) return beneficiaries;
    return beneficiaries.filter(
      (b) =>
        `${b.first_name} ${b.last_name}`.toLowerCase().includes(term) ||
        b.country.toLowerCase().includes(term),
    );
  }, [recipientSearch, beneficiaries]);

  const selectedBeneficiary = beneficiaries.find((b) => String(b.id) === recipientId) ?? null;

  function applyCoupon() {
    const code = couponInput.trim();
    if (!code) return;
    // 1. Personal coupon takes priority — has user-specific price
    if (
      myCoupon?.name &&
      myCoupon.name.toLowerCase() === code.toLowerCase() &&
      (myCoupon.remaining ?? 0) > 0
    ) {
      setAppliedCoupon(toApiCoupon(myCoupon, coupons));
      setCouponError("");
      return;
    }
    // 2. Platform coupon
    const match = coupons.find((c) => c.name.toLowerCase() === code.toLowerCase());
    if (match && match.remaining > 0) {
      setAppliedCoupon(match);
      setCouponError("");
      return;
    }
    setAppliedCoupon(null);
    setCouponError(t("calculator.invalidCoupon"));
  }

  function removeCoupon() {
    setAppliedCoupon(null);
    setCouponInput("");
    setCouponError("");
  }

  async function next() {
    const e: Record<string, string> = {};
    if (step === 0) {
      const amt = parseFloat(sendAmount);
      if (!amt || amt <= 0) e.amount = t("app.send.errors.amountRequired");
      if (from === to) e.amount = t("app.send.errors.amountRequired");
    }
    if (step === 1 && !recipientId) e.recipient = t("app.send.errors.recipientRequired");
    if (step === 2 && (!sendingPurpose || !sourceOfFund || !paymentGateway)) e.payment = t("app.send.errors.paymentRequired");
    setErrors(e);
    if (Object.keys(e).length > 0) return;

    if (step === 0) {
      const senderCur = senderCurrencies.find((c) => c.code === from);
      const receiverCur = receiverCurrencies.find((c) => c.code === to);
      const slug = TX_TYPE_SLUG[method];
      const txType = txTypes.find((t) => t.slug === slug) ?? txTypes[0];
      if (!senderCur || !receiverCur || !txType) return;
      setStepLoading(true);
      try {
        const data = await sendRemittanceApi.store({
          type: txType.title,
          send_money: sendAmount,
          sender_currency: senderCur.id,
          receiver_currency: receiverCur.id,
          ...(appliedCoupon ? { coupon: appliedCoupon.name } : {}),
        });
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const td = (data as any)?.temporary_data ?? {};
        const id = td?.identifier ?? "";
        const receiverCountry = td?.data?.receiver_country ?? "";
        setIdentifier(id);
        if (id) localStorage.setItem("send_identifier", id);
        if (receiverCountry) localStorage.setItem("send_receiver_country", receiverCountry);
      } catch (err) {
        const message = isApiError(err) ? err.message : t("app.send.errors.amountRequired");
        if (appliedCoupon && message.toLowerCase().includes("coupon")) {
          setAppliedCoupon(null);
          setCouponError(message);
        } else {
          setErrors({ amount: message });
        }
        return;
      } finally {
        setStepLoading(false);
      }
    }

    if (step === 1) {
      if (!recipientId || !identifier) return;
      setStepLoading(true);
      try {
        await sendRemittanceApi.attachBeneficiary({
          beneficiary_id: recipientId,
          identifier,
        });
      } catch {
        setErrors({ recipient: t("app.send.errors.recipientRequired") });
        return;
      } finally {
        setStepLoading(false);
      }
    }

    if (step === 2) {
      if (!sendingPurpose || !sourceOfFund || !paymentGateway || !identifier) return;
      setStepLoading(true);
      try {
        await sendRemittanceApi.storeReceiptPayment(identifier, {
          sending_purpose: sendingPurpose,
          source: sourceOfFund,
          remark: remarks,
          payment_gateway: paymentGateway,
        });
      } catch {
        setErrors({ payment: t("app.send.errors.paymentRequired") });
        return;
      } finally {
        setStepLoading(false);
      }
    }

    navigateToStep(Math.min(3, step + 1));
  }

  async function confirm() {
    if (!terms) {
      setErrors({ terms: t("app.send.errors.termsRequired") });
      return;
    }
    if (!identifier) return;
    setErrors({});

    if (/-manual/i.test(selectedGateway?.alias ?? "")) {
      router.push(`/send/manual?identifier=${encodeURIComponent(identifier)}&alias=${encodeURIComponent(selectedGateway?.alias ?? "")}`);
      return;
    }

    setSubmitting(true);
    try {
      const origin = window.location.origin;
      const isAuthorizeGateway = /authorize/i.test(selectedGateway?.alias ?? "");
      // success_url: the just-created transfer lands here in the list — the SPA's
      // in-memory success screen can't survive a full external redirect back from
      // the hosted gateway page. cancel_url: back to this exact review step (step 4),
      // with the identifier already cached, so the user can retry without restarting.
      // Backend requires both keys present on every call regardless of gateway (omitting
      // them throws an "Undefined array key" PHP warning) — so always send them; the
      // Authorize.net 302-vs-JSON difference is caused by something else server-side,
      // not by success_url's presence.
      const data = (await sendRemittanceApi.submit({
        identifier,
        success_url: `${origin}/transactions`,
        cancel_url: `${origin}/send?step=4`,
      })) as {
        trx_id?: string;
        trx?: string;
        reference?: string;
        identifier?: string;
        redirect_url?: string;
      };
      const trxId: string = data?.trx_id ?? data?.trx ?? data?.reference ?? identifier;

      if (/tatum/i.test(selectedGateway?.alias ?? "")) {
        router.push(`/send/crypto?trxId=${encodeURIComponent(trxId)}&alias=${encodeURIComponent(selectedGateway?.alias ?? "")}`);
        return;
      }

      // Authorize.net's "redirect_url" is its own API endpoint (POST-only, card
      // capture) — not a hosted page to browse to. Collect card details on our own
      // page and post them via authorizePayment() instead of following the URL.
      // submit-data's response carries a fresh identifier scoped to this payment
      // step — the redirect_url's query string is built from that one, not the
      // original step-0 identifier — so that's what authorize-payment-submit needs.
      if (isAuthorizeGateway) {
        const authorizeIdentifier: string = data?.identifier ?? identifier;
        router.push(`/send/authorize?identifier=${encodeURIComponent(authorizeIdentifier)}`);
        return;
      }

      const redirectUrl: string | undefined = data?.redirect_url;
      if (redirectUrl) {
        window.location.href = redirectUrl;
        return;
      }
      setSuccessId(trxId);
      setSuccessRef(trxId);
    } catch (err) {
      let msg = t("app.send.errors.paymentRequired");
      if (isApiError(err)) {
        const raw = err.raw as Record<string, unknown> | undefined;
        const errArr = (raw?.message as Record<string, unknown> | undefined)?.error;
        msg = (Array.isArray(errArr) && typeof errArr[0] === "string" ? errArr[0] : null) ?? err.message;
      } else if (err instanceof Error) {
        msg = err.message;
      }
      setErrors({ terms: msg });
    } finally {
      setSubmitting(false);
    }
  }

  if (successId) {
    return (
      <Container>
        <Card>
          <CardContent>
            <div className="text-center">
              <span className="mx-auto grid h-16 w-16 place-items-center rounded-2xl bg-gradient-to-br from-accent-100 to-accent-50 text-accent-600 animate-scale-in dark:from-accent-500/25 dark:to-accent-500/5 dark:text-accent-300">
                <CheckCircle2 className="h-8 w-8" />
              </span>
              <h1 className="mt-6 text-2xl font-bold text-slate-900 dark:text-white">
                {t("app.send.successTitle")}
              </h1>
              <p className="mt-2 text-sm text-slate-600 dark:text-slate-300">
                {t("app.send.successBody")}
              </p>
              <div className="mt-6 inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-4 py-2 text-xs font-semibold tabular-nums text-slate-700 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200">
                {t("app.send.reference")} · {successRef}
              </div>
              <div className="mt-8 flex flex-col gap-3 sm:flex-row sm:justify-center">
                <ButtonLink href={transactionDetailHref(successId)} prefetch={false} size="md">
                  {t("app.send.trackTransfer")}
                  <ArrowUpRight className="h-4 w-4 rtl:-scale-x-100" />
                </ButtonLink>
                <Button
                  type="button"
                  variant="outline"
                  size="md"
                  onClick={() => {
                    navigateToStep(0);
                    setSuccessId(null);
                    setSuccessRef("");
                    setRecipientId("");
                    setTerms(false);
                  }}
                >
                  {t("app.send.sendAnother")}
                </Button>
              </div>
            </div>
          </CardContent>
        </Card>
      </Container>
    );
  }

  if (kycGateStatus) {
    return <KycGate status={kycGateStatus} />;
  }

  return (
    <Container className="max-w-full space-y-6">
      <header>
        <h1 className="text-2xl font-bold text-slate-900 dark:text-white sm:text-3xl">
          {t("app.send.title")}
        </h1>
        <p className="mt-1 text-sm text-slate-600 dark:text-slate-300">
          {t("app.send.subtitle")}
        </p>
      </header>

      <Stepper steps={stepDefs} current={step} />

      {/* Unified Block at the Row (`Card` surrounding both left and right columns) */}
      <Card className="overflow-hidden rounded-3xl border-slate-200/80 bg-white/95 shadow-elevated dark:border-white/10 dark:bg-slate-900/90">
        <div className="grid lg:grid-cols-12 divide-y lg:divide-y-0 lg:divide-x divide-slate-200/80 dark:divide-white/10">
          {/* Left Column — Options & Form Steps */}
          <div className="lg:col-span-7 xl:col-span-8 p-6 sm:p-8">
            {step === 0 && (
            <div className="space-y-4">
              <h2 className="text-lg font-bold text-slate-900 dark:text-white">
                {t("app.send.step1Title")}
              </h2>
              <Field label={t("calculator.youSend")} required>
                <div ref={senderRef} className="flex items-stretch overflow-hidden rounded-2xl border border-slate-200 bg-white focus-within:border-brand-400 focus-within:shadow-glow-sm dark:border-slate-700 dark:bg-slate-900/60">
                  <input
                    type="number"
                    inputMode="decimal"
                    min={1}
                    placeholder="0.00"
                    value={sendAmount}
                    onChange={(e) => setSendAmount(e.target.value)}
                    aria-label={t("calculator.youSend")}
                    className="min-w-0 flex-1 bg-transparent px-4 py-3.5 text-xl font-bold tabular-nums outline-none dark:text-white"
                  />
                  <ReactSelect<CurrencyOpt>
                    unstyled
                    inputValue={senderSearch}
                    menuIsOpen={senderMenu.menuIsOpen}
                    onMenuOpen={senderMenu.onMenuOpen}
                    onInputChange={(val, { action }) => {
                      if (action === "input-change") setSenderSearch(val);
                    }}
                    onMenuClose={() => { senderMenu.onMenuClose(); setSenderSearch(""); }}
                    aria-label={t("calculator.sendCurrency")}
                    menuPortalTarget={typeof document !== "undefined" ? document.body : undefined}
                    menuPosition="fixed"
                    styles={{
                      menuPortal: (base) => {
                        const rect = senderRef.current?.getBoundingClientRect();
                        return { ...base, zIndex: 9999, ...(rect ? { left: rect.left, width: rect.width } : {}) };
                      },
                    }}
                    components={{ DropdownIndicator: TxDropdownIndicator, IndicatorSeparator: () => null, MenuList: CurrencyMenuList, Input: CurrencyInput }}
                    options={senderOptions}
                    value={senderOptions.find(o => o.value === from) ?? null}
                    onChange={(opt) => setFrom((opt as { value: string } | null)?.value ?? from)}
                    filterOption={(option, input) => {
                      if (!input) return true;
                      const lower = input.toLowerCase();
                      return option.label.toLowerCase().includes(lower) || option.value.toLowerCase().includes(lower);
                    }}
                    formatOptionLabel={(opt, { context }) =>
                      context === "menu" ? (
                        <div className="flex items-center gap-3">
                          {opt.flag ? (
                            <img src={opt.flag} alt="" aria-hidden className="h-7 w-7 shrink-0 rounded-full object-cover" />
                          ) : (
                            <span className="h-7 w-7 shrink-0 rounded-full bg-slate-200 dark:bg-slate-700" />
                          )}
                          <span className="flex-1 text-sm text-slate-900 dark:text-white">{opt.label}</span>
                          <span className="text-xs font-bold text-slate-400 dark:text-slate-500">{opt.value}</span>
                        </div>
                      ) : (
                        <div className="flex items-center gap-2">
                          {opt.flag && (
                            <img src={opt.flag} alt="" aria-hidden className="h-5 w-5 shrink-0 rounded-full object-cover" />
                          )}
                          <span className="text-sm font-bold text-white">{opt.value}</span>
                        </div>
                      )
                    }
                    classNames={{
                      container: () => "self-stretch shrink-0",
                      control: () => "flex h-full cursor-pointer items-center gap-2 bg-brand-600 px-3",
                      menu: () => "select-menu-portal z-[9999] mt-1 overflow-hidden rounded-xl border border-slate-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900",
                      option: ({ isFocused, isSelected }) =>
                        cn("cursor-pointer px-4 py-2.5", isSelected ? "bg-brand-50 dark:bg-brand-500/20" : isFocused ? "bg-slate-50 dark:bg-slate-800" : ""),
                      menuList: () => "max-h-64 overflow-y-auto py-1",
                      input: () => "sr-only",
                      placeholder: () => "text-white/60 text-sm",
                    }}
                  />
                </div>
              </Field>
              <Field label={t("calculator.recipientGets")} required>
                <div ref={receiverRef} className="flex items-stretch overflow-hidden rounded-2xl border border-slate-200 bg-gradient-to-br from-brand-50/40 to-accent-50/30 dark:border-slate-700 dark:from-brand-500/5 dark:to-accent-500/5">
                  <div className="flex flex-1 items-center px-4 py-3.5 text-xl font-bold tabular-nums dark:text-white">
                    {formatNumber(result.receive)}
                  </div>
                  <ReactSelect<CurrencyOpt>
                    unstyled
                    inputValue={receiverSearch}
                    menuIsOpen={receiverMenu.menuIsOpen}
                    onMenuOpen={receiverMenu.onMenuOpen}
                    onInputChange={(val, { action }) => {
                      if (action === "input-change") setReceiverSearch(val);
                    }}
                    onMenuClose={() => { receiverMenu.onMenuClose(); setReceiverSearch(""); }}
                    aria-label={t("calculator.receiveCurrency")}
                    menuPortalTarget={typeof document !== "undefined" ? document.body : undefined}
                    menuPosition="fixed"
                    styles={{
                      menuPortal: (base) => {
                        const rect = receiverRef.current?.getBoundingClientRect();
                        return { ...base, zIndex: 9999, ...(rect ? { left: rect.left, width: rect.width } : {}) };
                      },
                    }}
                    components={{ DropdownIndicator: TxDropdownIndicator, IndicatorSeparator: () => null, MenuList: CurrencyMenuList, Input: CurrencyInput }}
                    options={receiverOptions}
                    value={receiverOptions.find(o => o.value === to) ?? null}
                    onChange={(opt) => setTo((opt as { value: string } | null)?.value ?? to)}
                    filterOption={(option, input) => {
                      if (!input) return true;
                      const lower = input.toLowerCase();
                      return option.label.toLowerCase().includes(lower) || option.value.toLowerCase().includes(lower);
                    }}
                    formatOptionLabel={(opt, { context }) =>
                      context === "menu" ? (
                        <div className="flex items-center gap-3">
                          {opt.flag ? (
                            <img src={opt.flag} alt="" aria-hidden className="h-7 w-7 shrink-0 rounded-full object-cover" />
                          ) : (
                            <span className="h-7 w-7 shrink-0 rounded-full bg-slate-200 dark:bg-slate-700" />
                          )}
                          <span className="flex-1 text-sm text-slate-900 dark:text-white">{opt.label}</span>
                          <span className="text-xs font-bold text-slate-400 dark:text-slate-500">{opt.value}</span>
                        </div>
                      ) : (
                        <div className="flex items-center gap-2">
                          {opt.flag && (
                            <img src={opt.flag} alt="" aria-hidden className="h-5 w-5 shrink-0 rounded-full object-cover" />
                          )}
                          <span className="text-sm font-bold text-white">{opt.value}</span>
                        </div>
                      )
                    }
                    classNames={{
                      container: () => "self-stretch shrink-0",
                      control: () => "flex h-full cursor-pointer items-center gap-2 bg-brand-600 px-3",
                      menu: () => "select-menu-portal z-[9999] mt-1 overflow-hidden rounded-xl border border-slate-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900",
                      option: ({ isFocused, isSelected }) =>
                        cn("cursor-pointer px-4 py-2.5", isSelected ? "bg-brand-50 dark:bg-brand-500/20" : isFocused ? "bg-slate-50 dark:bg-slate-800" : ""),
                      menuList: () => "max-h-64 overflow-y-auto py-1",
                      input: () => "sr-only",
                      placeholder: () => "text-slate-400 text-sm",
                    }}
                  />
                </div>
              </Field>
              <Field label={t("calculator.receiveMethod")} required>
                <div role="radiogroup" className="grid grid-cols-3 gap-2">
                  {(txTypes.length > 0 ? txTypes : FALLBACK_TX_TYPES).map((tx) => {
                    const Icon = SLUG_ICON[tx.slug] ?? Building2;
                    const txMethod = SLUG_METHOD[tx.slug] ?? "bank";
                    const active = method === txMethod;
                    return (
                      <button
                        key={tx.slug}
                        type="button"
                        role="radio"
                        aria-checked={active}
                        onClick={() => setMethod(txMethod)}
                        className={cn(
                          "flex flex-col items-center gap-1.5 rounded-2xl border px-2 py-3 text-xs font-medium transition",
                          active
                            ? "border-brand-500 bg-brand-50 text-brand-700 shadow-soft dark:border-brand-400/50 dark:bg-brand-500/15 dark:text-brand-300"
                            : "border-slate-200 bg-white text-slate-600 hover:border-slate-300 dark:border-slate-700 dark:bg-slate-900/60 dark:text-slate-300",
                        )}
                      >
                        <Icon className="h-4 w-4" />
                        {tx.title}
                      </button>
                    );
                  })}
                </div>
              </Field>
              {(availablePromos.length > 0 || appliedCoupon) && (
              <Field label={t("calculator.haveCoupon")}>
                {appliedCoupon ? (
                  <div className="flex items-center justify-between gap-3 rounded-xl border border-emerald-300/80 bg-gradient-to-r from-emerald-50 via-teal-50 to-cyan-50 px-3.5 py-2 shadow-sm transition hover:border-emerald-400 dark:border-emerald-500/40 dark:from-emerald-500/15 dark:via-teal-500/15 dark:to-cyan-500/15">
                    <div className="flex items-center gap-2 text-xs text-emerald-950 dark:text-emerald-100">
                      <CheckCircle2 className="h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden />
                      <span>
                        Applied promo:{" "}
                        <strong className="font-heading font-bold text-emerald-700 dark:text-emerald-300">
                          {appliedCoupon.name}
                        </strong>
                      </span>
                    </div>
                    <button
                      type="button"
                      onClick={removeCoupon}
                      aria-label={t("calculator.couponApplied")}
                      className="inline-flex shrink-0 items-center gap-1 rounded-lg bg-emerald-500/15 px-2.5 py-1 text-xs font-bold text-emerald-700 transition hover:bg-emerald-500/25 dark:bg-emerald-400/20 dark:text-emerald-300 dark:hover:bg-emerald-400/30"
                    >
                      <X className="h-3.5 w-3.5" aria-hidden />
                      {t("calculator.removeCoupon")}
                    </button>
                  </div>
                ) : (
                  <>
                    <div className="flex items-center gap-2 rounded-2xl border border-slate-200/80 bg-white/90 p-1.5 shadow-sm transition-all focus-within:border-brand-500 focus-within:ring-2 focus-within:ring-brand-500/20 dark:border-white/10 dark:bg-slate-900/80">
                      <input
                        type="text"
                        value={couponInput}
                        onChange={(e) => {
                          setCouponInput(e.target.value);
                          setCouponError("");
                        }}
                        onKeyDown={(e) => {
                          if (e.key === "Enter") {
                            e.preventDefault();
                            applyCoupon();
                          }
                        }}
                        placeholder={t("calculator.couponCode")}
                        aria-label={t("calculator.couponCode")}
                        aria-invalid={couponError ? true : undefined}
                        className="h-10 flex-1 bg-transparent px-3 text-sm font-semibold uppercase tracking-wider text-slate-900 placeholder:normal-case placeholder:font-normal placeholder:tracking-normal placeholder:text-slate-400 outline-none dark:text-white"
                      />
                      <Button
                        type="button"
                        size="sm"
                        onClick={applyCoupon}
                        disabled={!couponInput.trim()}
                        className="rounded-xl px-4 text-xs font-semibold"
                      >
                        {t("calculator.applyCoupon")}
                      </Button>
                    </div>
                    {/* Available Promo Auto Slider — user can pick & apply from multiple promo sources */}
                    {availablePromos.length > 0 && currentPromo && (
                      <div className="mt-4 overflow-hidden rounded-xl border border-amber-300/80 bg-gradient-to-r from-amber-50 via-orange-50 to-yellow-50 px-3.5 py-2.5 shadow-sm transition-all dark:border-amber-500/40 dark:from-amber-500/15 dark:via-orange-500/15 dark:to-yellow-500/15">
                        <div className="flex flex-wrap items-center justify-between gap-2">
                          <div className="flex min-w-0 flex-wrap items-center gap-1.5 text-xs text-amber-950 dark:text-amber-100">
                            <Sparkles className="h-3.5 w-3.5 shrink-0 text-amber-600 animate-pulse dark:text-amber-400" aria-hidden />
                            <span className="shrink-0 font-medium text-slate-600 dark:text-slate-300">{t("calculator.availablePromo")}</span>
                            <strong className="min-w-0 truncate font-heading font-bold text-amber-600 transition-all dark:text-amber-300">
                              {currentPromo.name}
                            </strong>
                            <span className="shrink-0 rounded bg-amber-500/15 px-1.5 py-0.5 text-[10px] font-bold text-amber-800 dark:bg-amber-400/20 dark:text-amber-200">
                              {currentPromo.discountDesc}
                            </span>
                          </div>
                          <div className="flex shrink-0 items-center gap-2">
                            {availablePromos.length > 1 && (
                              <div className="flex items-center gap-0.5 me-1">
                                <button
                                  type="button"
                                  onClick={() => setActivePromoIndex((prev) => (prev - 1 + availablePromos.length) % availablePromos.length)}
                                  className="flex h-5 w-5 items-center justify-center rounded text-amber-700 hover:bg-amber-500/20 dark:text-amber-300 dark:hover:bg-amber-400/20"
                                  aria-label={t("calculator.previousPromo")}
                                >
                                  &lsaquo;
                                </button>
                                <span className="text-[10px] font-bold tabular-nums text-amber-800/80 dark:text-amber-200/80">
                                  {(activePromoIndex % availablePromos.length) + 1}/{availablePromos.length}
                                </span>
                                <button
                                  type="button"
                                  onClick={() => setActivePromoIndex((prev) => (prev + 1) % availablePromos.length)}
                                  className="flex h-5 w-5 items-center justify-center rounded text-amber-700 hover:bg-amber-500/20 dark:text-amber-300 dark:hover:bg-amber-400/20"
                                  aria-label={t("calculator.nextPromo")}
                                >
                                  &rsaquo;
                                </button>
                              </div>
                            )}
                            <button
                              type="button"
                              onClick={() => {
                                setAppliedCoupon(currentPromo.apiCoupon);
                                setCouponInput(currentPromo.name);
                                setCouponError("");
                              }}
                              className="inline-flex shrink-0 items-center gap-1 rounded-lg bg-amber-500/20 px-2.5 py-1 text-xs font-bold text-amber-800 transition hover:bg-amber-500/30 dark:bg-amber-400/20 dark:text-amber-300 dark:hover:bg-amber-400/30"
                            >
                              {t("calculator.quickApply")}
                              <ArrowRight className="h-3 w-3 rtl:-scale-x-100" aria-hidden />
                            </button>
                          </div>
                        </div>
                        {availablePromos.length > 1 && (
                          <div className="mt-2 flex flex-wrap items-center gap-1.5 border-t border-dashed border-amber-200/60 pt-2 dark:border-amber-500/20">
                            <span className="text-[10px] font-semibold text-amber-800/70 me-1 dark:text-amber-300/70">{t("calculator.pickOption")}</span>
                            {availablePromos.map((promo, idx) => {
                              const isActive = (activePromoIndex % availablePromos.length) === idx;
                              return (
                                <button
                                  key={promo.id}
                                  type="button"
                                  onClick={() => setActivePromoIndex(idx)}
                                  className={cn(
                                    "rounded-md px-2 py-0.5 text-[11px] font-bold transition-all",
                                    isActive
                                      ? "bg-amber-500 text-white shadow-sm dark:bg-amber-400 dark:text-slate-950"
                                      : "bg-amber-500/10 text-amber-800 hover:bg-amber-500/20 dark:bg-amber-400/10 dark:text-amber-300 dark:hover:bg-amber-400/20",
                                  )}
                                >
                                  {promo.name}
                                </button>
                              );
                            })}
                          </div>
                        )}
                      </div>
                    )}
                    {couponError && (
                      <p className="mt-1.5 text-xs font-medium text-rose-600 dark:text-rose-400">{couponError}</p>
                    )}
                  </>
                )}
              </Field>
              )}
              <div className="block lg:hidden">
                <Summary result={result} from={from} to={to} selectedGateway={selectedGateway} />
              </div>
              {errors.amount && (
                <p className="text-xs font-medium text-rose-600">{errors.amount}</p>
              )}
            </div>
          )}

          {step === 1 && (
            <div className="space-y-4">
              <h2 className="text-lg font-bold text-slate-900 dark:text-white">
                {t("app.send.step2Title")}
              </h2>

              {beneficiaryLoading ? (
                <div className="grid grid-cols-1 gap-3.5 sm:grid-cols-2">
                  {[0, 1, 2, 3].map((i) => (
                    <div
                      key={i}
                      className="h-[114px] animate-pulse rounded-2xl border border-slate-200 bg-slate-100 dark:border-slate-700 dark:bg-slate-800"
                    />
                  ))}
                </div>
              ) : beneficiaries.length === 0 ? (
                <EmptyState
                  icon={Plus}
                  title={t("app.send.noRecipients")}
                  action={
                    <ButtonLink href={sendNewBeneficiaryHref(identifier)}>
                      <Plus className="h-4 w-4" />
                      {t("app.send.addNewRecipient")}
                    </ButtonLink>
                  }
                />
              ) : (
                <>
                  <div className="relative">
                    <Search className="pointer-events-none absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" />
                    <input
                      type="search"
                      value={recipientSearch}
                      onChange={(e) => setRecipientSearch(e.target.value)}
                      placeholder={t("app.send.searchRecipientsPlaceholder")}
                      className="h-11 w-full rounded-2xl border border-slate-200 bg-white ps-9 pe-4 text-sm outline-none focus:border-brand-400 dark:border-slate-700 dark:bg-slate-900 dark:text-white"
                    />
                  </div>
                  <div role="radiogroup" className="grid grid-cols-1 gap-3.5 sm:grid-cols-2">
                    {filtered.map((b) => (
                      <BeneficiaryChoice
                        key={b.id}
                        beneficiary={b}
                        selected={String(b.id) === recipientId}
                        onSelect={() => setRecipientId(String(b.id))}
                      />
                    ))}
                  </div>
                  <Link
                    href={sendNewBeneficiaryHref(identifier)}
                    className="mt-2 inline-flex items-center gap-2 rounded-2xl border border-dashed border-slate-300 px-4 py-3 text-sm font-semibold text-brand-600 transition hover:border-brand-400 hover:bg-brand-50 dark:border-slate-700 dark:text-brand-300 dark:hover:border-brand-400 dark:hover:bg-brand-500/10"
                  >
                    <Plus className="h-4 w-4" />
                    {t("app.send.addNewRecipient")}
                  </Link>
                  {errors.recipient && (
                    <p className="text-xs font-medium text-rose-600">{errors.recipient}</p>
                  )}
                </>
              )}
            </div>
          )}

          {step === 2 && (
            <div className="space-y-5">
              <h2 className="text-lg font-bold text-slate-900 dark:text-white">
                {t("app.send.step3Title")}
              </h2>
              <div className="grid gap-4 sm:grid-cols-2">
                <div className="space-y-1.5">
                  <label className="block text-[length:var(--text-sm-fixed)] font-semibold capitalize tracking-wider text-slate-500 dark:text-slate-400">
                    Sending Purpose <span className="text-rose-500">*</span>
                  </label>
                  <ReactSelect
                    unstyled
                    isSearchable={false}
                    isLoading={receiptPaymentLoading}
                    components={{ DropdownIndicator: TxDropdownIndicator, IndicatorSeparator: () => null }}
                    options={purposeOptions}
                    placeholder="Select Purpose"
                    value={purposeOptions.find((o) => o.value === sendingPurpose) ?? null}
                    onChange={(opt) => setSendingPurpose((opt as { value: string } | null)?.value ?? "")}
                    classNames={STEP2_SELECT_CLS}
                  />
                </div>
                <div className="space-y-1.5">
                  <label className="block text-[length:var(--text-sm-fixed)] font-semibold capitalize tracking-wider text-slate-500 dark:text-slate-400">
                    Source Of Fund <span className="text-rose-500">*</span>
                  </label>
                  <ReactSelect
                    unstyled
                    isSearchable={false}
                    isLoading={receiptPaymentLoading}
                    components={{ DropdownIndicator: TxDropdownIndicator, IndicatorSeparator: () => null }}
                    options={sourceOptions}
                    placeholder="Select Source"
                    value={sourceOptions.find((o) => o.value === sourceOfFund) ?? null}
                    onChange={(opt) => setSourceOfFund((opt as { value: string } | null)?.value ?? "")}
                    classNames={STEP2_SELECT_CLS}
                  />
                </div>
              </div>
              <div className="space-y-1.5">
                <label className="block text-[length:var(--text-sm-fixed)] font-semibold capitalize tracking-wider text-slate-500 dark:text-slate-400">
                  Remarks{" "}
                  <span className="font-normal normal-case tracking-normal text-slate-400">
                    (Optional)
                  </span>
                </label>
                <textarea
                  value={remarks}
                  onChange={(e) => setRemarks(e.target.value)}
                  rows={3}
                  placeholder="Write Here..."
                  className="w-full rounded-2xl border border-slate-200 bg-white px-4 py-3 text-sm outline-none transition-colors placeholder:text-slate-400 focus:border-brand-400 dark:border-white/[0.08] dark:bg-white/[0.04] dark:text-white"
                />
              </div>
              <div className="space-y-1.5" ref={gatewayRef}>
                <label className="block text-[length:var(--text-sm-fixed)] font-semibold capitalize tracking-wider text-slate-500 dark:text-slate-400">
                  Payment Gateway <span className="text-rose-500">*</span>
                </label>
                <ReactSelect
                  unstyled
                  isLoading={receiptPaymentLoading}
                  inputValue={gatewaySearch}
                  menuIsOpen={gatewayMenu.menuIsOpen}
                  onMenuOpen={gatewayMenu.onMenuOpen}
                  onInputChange={(val, { action }) => { if (action === "input-change") setGatewaySearch(val); }}
                  onMenuClose={() => { gatewayMenu.onMenuClose(); setGatewaySearch(""); }}
                  menuPortalTarget={typeof document !== "undefined" ? document.body : undefined}
                  menuPosition="fixed"
                  styles={{ menuPortal: (base) => ({ ...base, zIndex: 9999 }) }}
                  components={{ DropdownIndicator: TxDropdownIndicator, IndicatorSeparator: () => null, MenuList: CurrencyMenuList, Input: CurrencyInput }}
                  options={gatewayOptions}
                  placeholder="Select Gateway"
                  value={gatewayOptions.find((o) => o.value === paymentGateway) ?? null}
                  onChange={(opt) => setPaymentGateway((opt as { value: string } | null)?.value ?? "")}
                  classNames={STEP2_SELECT_CLS}
                />
              </div>
              {errors.payment && (
                <p className="text-xs font-medium text-rose-600">{errors.payment}</p>
              )}
            </div>
          )}

          {step === 3 && selectedBeneficiary && (
            <div className="space-y-4">
              <h2 className="text-lg font-bold text-slate-900 dark:text-white">
                {t("app.send.reviewTitle")}
              </h2>

              <div className="grid gap-4 lg:grid-cols-2">
                <ReviewCard title="Remittance Summary">
                  <ReviewRow label="Sending Amount" value={`${formatNumber(result.send)} ${from}`} accent />
                  <ReviewRow
                    label="Exchange Rate"
                    value={`1 ${from} = ${formatNumber(result.rate, undefined, 2)} ${to}`}
                  />
                  <ReviewRow
                    label="Total Fees & Charges"
                    value={`${formatNumber(result.transferFee)} ${from}`}
                  />
                  <ReviewRow
                    label="Amount We Will Convert"
                    value={`${formatNumber(result.send)} ${from}`}
                  />
                  <ReviewRow label="Will Get Amount" value={`${formatNumber(result.receive)} ${to}`} accent />
                  <ReviewRow
                    label="Sending Purpose"
                    value={purposeOptions.find((o) => o.value === sendingPurpose)?.label ?? "—"}
                  />
                  <ReviewRow
                    label="Source Of Fund"
                    value={sourceOptions.find((o) => o.value === sourceOfFund)?.label ?? "—"}
                  />
                </ReviewCard>

                <ReviewCard title="Receipt Summary">
                  <ReviewRow
                    label="Recipient Name"
                    value={[selectedBeneficiary.first_name, selectedBeneficiary.middle_name, selectedBeneficiary.last_name].filter(Boolean).join(" ")}
                    accent
                  />
                  <ReviewRow label="Recipient Email" value={selectedBeneficiary.email ?? "—"} />
                  <ReviewRow label="Phone" value={selectedBeneficiary.phone ?? "—"} />
                  <ReviewRow label="Country" value={selectedBeneficiary.country} />
                  <ReviewRow
                    label="City & State"
                    value={[selectedBeneficiary.city, selectedBeneficiary.state].filter(Boolean).join(", ") || "—"}
                  />
                  <ReviewRow label="Zip Code" value={selectedBeneficiary.zip_code ?? "—"} />
                  <ReviewRow label="Address" value={selectedBeneficiary.address ?? "—"} />
                </ReviewCard>
              </div>

              <ReviewCard title="Transaction Info">
                <ReviewRow
                  label="Transaction Type"
                  value={t(
                    method === "bank"
                      ? "calculator.bankTransfer"
                      : method === "cash"
                        ? "calculator.cashPickup"
                        : "calculator.mobileMoney",
                  )}
                  accent
                />
                <ReviewRow
                  label="Account Number"
                  value={selectedBeneficiary.iban_number ?? selectedBeneficiary.account_number ?? "—"}
                />
                <ReviewRow
                  label="Exchange Rate"
                  value={
                    selectedGateway
                      ? `1 ${from} = ${formatNumber(parseFloat(selectedGateway.rate), undefined, 2)} ${selectedGateway.currency_code}`
                      : "—"
                  }
                />
              </ReviewCard>

              <ReviewCard title="Payment Info">
                <ReviewRow
                  label="Method Name"
                  value={selectedBeneficiary.bank_name ?? selectedBeneficiary.mobile_name ?? selectedBeneficiary.pickup_point ?? "—"}
                />
                <ReviewRow
                  label="Payment Method"
                  value={selectedGateway?.name ?? "—"}
                  accent
                />
                <ReviewRow
                  label="Payable Amount"
                  value={
                    selectedGateway
                      ? `${formatNumber(result.totalToPay * parseFloat(selectedGateway.rate))} ${selectedGateway.currency_code}`
                      : `${formatNumber(result.totalToPay)} ${from}`
                  }
                  accent
                />
              </ReviewCard>

              <label className="flex items-center gap-3 py-1.5 cursor-pointer select-none group">
                <input
                  type="checkbox"
                  checked={terms}
                  onChange={(e) => setTerms(e.target.checked)}
                  className="h-4 w-4 rounded border-slate-300 text-brand-600 focus:ring-brand-500 cursor-pointer dark:border-slate-600 dark:bg-slate-800"
                />
                <span className="text-sm font-medium text-slate-700 transition-colors group-hover:text-slate-900 dark:text-slate-300 dark:group-hover:text-white">
                  {t("app.send.termsLabel")}
                </span>
              </label>
              {errors.terms && (
                <p className="text-xs font-medium text-rose-600">{errors.terms}</p>
              )}

              <Button
                type="button"
                size="lg"
                onClick={confirm}
                loading={submitting}
                disabled={submitting || !terms}
                className="w-full"
              >
                <SendIcon className="h-4 w-4" />
                {submitting ? t("app.send.processing") : "Confirm Payment"}
              </Button>
            </div>
          )}

          <div className="mt-4 flex items-center justify-between gap-3">
            {step > 0 ? (
              <Button
                type="button"
                variant="outline"
                onClick={() => navigateToStep(Math.max(0, step - 1))}
                disabled={submitting || stepLoading}
              >
                <ArrowLeft className="h-4 w-4 rtl:-scale-x-100" />
                {t("app.send.back")}
              </Button>
            ) : (
              <span />
            )}
            {step < 3 && (
              <Button type="button" onClick={next} loading={stepLoading} disabled={stepLoading}>
                {t("app.send.next")}
                <ArrowRight className="h-4 w-4 rtl:-scale-x-100" />
              </Button>
            )}
          </div>
          </div>

          {/* Right Column — Live Summary (Unified inside the Row Block) */}
          <div className="hidden lg:block lg:col-span-5 xl:col-span-4 bg-slate-50/60 p-6 sm:p-8 dark:bg-slate-950/40">
            <h3 className="mb-5 font-heading text-lg font-bold text-slate-900 dark:text-white">
              {t("app.send.liveSummaryTitle")}
            </h3>
            <Summary result={result} from={from} to={to} clean selectedGateway={selectedGateway} />
          </div>
        </div>
      </Card>
    </Container>
  );
}

function Field({
  label,
  required,
  children,
}: {
  label: string;
  required?: boolean;
  children: React.ReactNode;
}) {
  return (
    <div>
      <label className="mb-2 block text-[length:var(--text-sm-fixed)] font-medium capitalize tracking-wider text-slate-500 dark:text-slate-400">
        {label} {required && <span className="text-rose-500">*</span>}
      </label>
      {children}
    </div>
  );
}

function Summary({
  result,
  from,
  to,
  clean = false,
  selectedGateway = null,
}: {
  result: SendResult;
  from: string;
  to: string;
  bold?: boolean;
  clean?: boolean;
  selectedGateway?: { rate: string; currency_code: string } | null;
}) {
  const { t } = useTranslation();
  const totalToPay = selectedGateway
    ? `${formatNumber(result.totalToPay * parseFloat(selectedGateway.rate))} ${selectedGateway.currency_code}`
    : `${formatNumber(result.totalToPay * result.rate)} ${to}`;
  return (
    <div
      className={
        clean
          ? "overflow-hidden rounded-2xl border border-slate-200/80 bg-white shadow-sm dark:border-white/10 dark:bg-slate-900/60"
          : "overflow-hidden rounded-3xl border border-slate-200/80 bg-white/90 shadow-[0_10px_30px_rgba(15,23,42,0.04)] backdrop-blur-xl dark:border-white/10 dark:bg-slate-900/80 dark:shadow-[0_0_40px_rgba(51,204,221,0.06)]"
      }
    >
      {/* Main details */}
      <div className="space-y-2 px-6 py-5">
        <Row label={t("app.send.summarySend")} value={`${formatNumber(result.send)} ${from}`} />
        <Row
          label={t("app.send.summaryRate")}
          value={`1 ${from} = ${formatNumber(result.rate, undefined, 2)} ${to}`}
        />
      </div>

      {/* Recipient receives — vibrant accent highlight box */}
      <div className="relative overflow-hidden border-y border-emerald-200/80 bg-gradient-to-r from-emerald-50 via-teal-50/90 to-emerald-50/70 px-6 py-3.5 dark:border-emerald-500/30 dark:from-emerald-950/60 dark:via-teal-950/50 dark:to-emerald-950/60">
        <div className="pointer-events-none absolute -right-8 -top-8 h-24 w-24 rounded-full bg-emerald-500/15 blur-2xl dark:bg-emerald-400/20" aria-hidden />
        <div className="relative flex items-center justify-between">
          <span className="font-heading text-sm font-bold tracking-wide text-emerald-800 dark:text-emerald-300">
            {t("app.send.summaryReceive")}
          </span>
          <span className="font-heading text-base font-bold tabular-nums tracking-tight text-emerald-600 drop-shadow-sm dark:text-emerald-400">
            {formatNumber(result.receive)} {to}
          </span>
        </div>
      </div>

      {/* Fee breakdown */}
      <div className="space-y-2 bg-slate-50/70 px-6 pb-5 pt-4 dark:bg-slate-950/40">
        <Row label={t("app.send.summaryFee")} value={`${formatNumber(result.transferFee)} ${from}`} muted />
        <Row label={t("app.send.summaryConversionAmount")} value={`${formatNumber(result.send)} ${from}`} muted />
        {result.discount > 0 && (
          <Row
            label={t("app.send.summaryDiscount")}
            value={`−${formatNumber(result.discount)} ${from}`}
            tone="positive"
          />
        )}
        <div className="my-3.5 h-px bg-gradient-to-r from-transparent via-slate-200 to-transparent dark:via-white/10" />
        {/* Total to pay */}
        <div className="flex items-center justify-between pt-1">
          <span className="font-heading text-sm font-bold text-slate-900 dark:text-white">{t("app.send.summaryTotal")}</span>
          <span className="font-heading text-lg font-black tabular-nums text-brand-600 dark:text-brand-400">
            {totalToPay}
          </span>
        </div>
      </div>
    </div>
  );
}

function Row({
  label,
  value,
  bold = false,
  muted = false,
  tone = "default",
}: {
  label: string;
  value: string;
  bold?: boolean;
  muted?: boolean;
  tone?: "default" | "positive";
}) {
  return (
    <div className="flex items-center justify-between py-1">
      <span
        className={cn(
          "text-sm font-medium",
          muted
            ? "text-slate-400 dark:text-slate-500"
            : "text-slate-600 dark:text-slate-400",
        )}
      >
        {label}
      </span>
      <span
        className={cn(
          "text-sm tabular-nums font-semibold",
          tone === "positive"
            ? "font-bold text-emerald-600 dark:text-emerald-400"
            : muted
              ? "text-slate-500 dark:text-slate-400"
              : "text-slate-900 dark:text-white",
          bold && "text-base font-bold",
        )}
      >
        {value}
      </span>
    </div>
  );
}

function BeneficiaryChoice({
  beneficiary: b,
  selected,
  onSelect,
}: {
  beneficiary: ApiBeneficiary;
  selected: boolean;
  onSelect: () => void;
}) {
  const country = COUNTRIES.find((c) => c.name === b.country);
  const initials = `${b.first_name.charAt(0)}${b.last_name.charAt(0)}`.toUpperCase();
  const detail = b.bank_name ?? b.mobile_name ?? b.account_number ?? "";
  return (
    <button
      type="button"
      role="radio"
      aria-checked={selected}
      onClick={onSelect}
      className={cn(
        "group relative flex flex-col justify-between rounded-2xl border p-4 text-start transition-all duration-200 cursor-pointer shadow-sm",
        selected
          ? "border-brand-500 bg-brand-50/70 shadow-sm dark:border-brand-400 dark:bg-brand-950/40"
          : "border-slate-200/80 bg-white hover:-translate-y-0.5 hover:border-brand-400 hover:shadow-md dark:border-white/10 dark:bg-slate-900/80 dark:hover:border-brand-500/50",
      )}
    >
      {/* Card Header: Avatar, Name & Check/Radio Indicator */}
      <div className="flex w-full items-center justify-between gap-3 mb-3">
        <div className="flex items-center gap-3 min-w-0">
          <span className="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-gradient-to-br from-brand-500 to-cyan-600 text-sm font-bold text-white shadow-sm">
            {initials}
          </span>
          <div className="min-w-0 flex-1">
            <p className="font-heading truncate text-base font-bold text-slate-900 group-hover:text-brand-600 transition-colors dark:text-white dark:group-hover:text-brand-400">
              {b.first_name} {b.last_name}
            </p>
            {country && (
              <span className="inline-flex items-center gap-1 text-xs font-semibold text-slate-500 dark:text-slate-400">
                <span className="text-sm" aria-hidden>{country.flag}</span>
                <span>{b.country}</span>
              </span>
            )}
          </div>
        </div>
        <div
          className={cn(
            "flex h-6 w-6 shrink-0 items-center justify-center rounded-full border transition-all",
            selected
              ? "border-brand-500 bg-brand-500 text-white shadow-sm dark:border-brand-400 dark:bg-brand-400 dark:text-slate-950"
              : "border-slate-300 group-hover:border-brand-400 dark:border-slate-600",
          )}
        >
          {selected && <Check className="h-3.5 w-3.5 stroke-2" />}
        </div>
      </div>

      {/* Card Footer: Method & Bank Details in a sleek inset box (replacing dividing line) */}
      <div
        className={cn(
          "mt-2 flex w-full flex-wrap items-center justify-between gap-2 rounded-xl px-3 py-2 text-xs transition-colors",
          selected
            ? "bg-brand-500/10 text-brand-950 dark:bg-brand-400/10 dark:text-brand-100"
            : "bg-slate-100/70 text-slate-600 dark:bg-white/[0.05] dark:text-slate-300",
        )}
      >
        <span className="inline-flex items-center gap-1.5 font-semibold">
          <Building2 className="h-3.5 w-3.5 text-brand-500 dark:text-brand-400" />
          {b.method}
        </span>
        {detail && (
          <span className="font-medium">{detail}</span>
        )}
      </div>
    </button>
  );
}

function ReviewCard({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div className="overflow-hidden rounded-2xl border border-slate-200/70 dark:border-slate-800">
      <div className="border-b border-slate-200/70 bg-slate-50/60 px-5 py-3.5 dark:border-slate-800 dark:bg-slate-900/40">
        <h3 className="text-sm font-bold text-slate-700 dark:text-white">{title}</h3>
      </div>
      {children}
    </div>
  );
}

function ReviewRow({
  label,
  value,
  accent = false,
}: {
  label: string;
  value: string;
  accent?: boolean;
}) {
  return (
    <div className="flex items-center justify-between gap-6 px-5 py-3 [&:not(:last-child)]:border-b [&:not(:last-child)]:border-slate-100 [&:not(:last-child)]:dark:border-slate-800/60">
      <span className="shrink-0 text-sm text-slate-500 dark:text-slate-400">{label}</span>
      <span
        className={cn(
          "text-end text-sm font-semibold tabular-nums",
          accent ? "text-brand-600 dark:text-brand-400" : "text-slate-900 dark:text-white",
        )}
      >
        {value}
      </span>
    </div>
  );
}
