"use client";

import type {
  PaymentOfframpQuoteRequest,
  PaymentRampInstruction,
  PaymentTransferSummary,
} from "@sdp/types";
import { BanknoteIcon, DollarSignIcon, WalletIcon } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import useSWR from "swr";
import {
  createTransfer,
  fetchTransferByProviderReference,
} from "@/app/dashboard/payments/payments-workspace.data";
import type { MessageKey, TranslationValues } from "@/i18n/messages";
import { useLocale, useTranslations } from "@/i18n/provider";
import { OFFRAMP_PAIRS, toRampCryptoToken } from "@/lib/ramps";
import type { WizardSummaryDetail } from "../../wizard-summary-list";
import { sourceWalletSchema, withdrawAmountSchema, withdrawSelectionSchema } from "../schema";
import {
  memoSummaryDetails,
  optionalDetail,
  providerSummaryDetail,
  summaryAmount,
} from "../wizard-summary";
import {
  isTerminalRampTransferStatus,
  type RampWizardStep,
  type UseRampWizardProps,
  useRampWizard,
} from "./use-ramp-wizard";

type CryptoDepositInstruction = Extract<PaymentRampInstruction, { kind: "crypto_deposit" }>;

function isCryptoDepositInstruction(
  instruction: PaymentRampInstruction
): instruction is CryptoDepositInstruction {
  return "kind" in instruction && instruction.kind === "crypto_deposit";
}

type Translate = (key: MessageKey, values?: TranslationValues) => string;
export type OfframpStepId = "WALLET" | "WITHDRAW" | "MEMO" | "COMPLETE" | "REQUIREMENTS";

export function getOfframpSteps(t: Translate): readonly RampWizardStep<OfframpStepId>[] {
  return [
    {
      id: "WALLET",
      label: t("DashboardPayments.ramps.offrampWalletStep"),
      title: t("DashboardPayments.ramps.offrampWalletTitle"),
    },
    {
      id: "WITHDRAW",
      label: t("DashboardPayments.ramps.offrampWithdrawStep"),
      title: t("DashboardPayments.ramps.offrampWithdrawTitle"),
    },
    {
      id: "MEMO",
      label: t("DashboardPayments.ramps.rampMemoStep"),
      title: t("DashboardPayments.ramps.rampMemoStepTitle"),
    },
    {
      id: "COMPLETE",
      label: t("DashboardPayments.ramps.offrampCompleteStep"),
      title: t("DashboardPayments.ramps.offrampCompleteTitle"),
    },
  ];
}

function getOfframpRequirementsStep(t: Translate): RampWizardStep<OfframpStepId> {
  return {
    id: "REQUIREMENTS",
    label: t("DashboardPayments.ramps.payoutDetailsStep"),
    title: t("DashboardPayments.ramps.payoutDetailsTitle"),
  };
}

export function useOfframpWizard(props: UseRampWizardProps) {
  const t = useTranslations();
  const locale = useLocale();
  const [onchainSendLoading, setOnchainSendLoading] = useState(false);
  const [onchainSendResult, setOnchainSendResult] = useState<PaymentTransferSummary | null>(null);
  const [quoteExpired, setQuoteExpired] = useState(false);

  const wizard = useRampWizard<OfframpStepId>(props, {
    pairs: OFFRAMP_PAIRS,
    steps: getOfframpSteps(t),
    stepSchemas: { WALLET: sourceWalletSchema, WITHDRAW: withdrawAmountSchema },
    quoteStepId: "MEMO",
    memoStepId: "MEMO",
    requirements: {
      step: getOfframpRequirementsStep(t),
      insertAfter: "MEMO",
      direction: "offramp",
    },
    advanceRequirementsBeforeQuote: true,
    selectionSchema: withdrawSelectionSchema,
    quoteEndpoint: "/api/dashboard/payments/ramps/offramp/quote",
    buildQuotePayload: ({ fields, provider, selectedRampPair, cryptoToken, rampsMemo }) =>
      ({
        provider,
        counterpartyId: fields.counterpartyId,
        sourceWallet: fields.walletId,
        cryptoToken,
        fiatCurrency: selectedRampPair.fiatCurrency,
        cryptoAmount: fields.amount.trim(),
        redirectUrl: `${window.location.origin}/dashboard/payments`,
        rampsMemo,
      }) satisfies PaymentOfframpQuoteRequest,
    onQuoteCreated: () => {
      setOnchainSendLoading(false);
      setOnchainSendResult(null);
      setQuoteExpired(false);
    },
  });

  const quoteExpiresAt =
    wizard.quote?.deliveryMode === "manual_instructions" && "expiresAt" in wizard.quote
      ? wizard.quote.expiresAt
      : undefined;

  useEffect(() => {
    if (!quoteExpiresAt) {
      return;
    }
    const remainingMs = Date.parse(quoteExpiresAt) - Date.now();
    if (!Number.isFinite(remainingMs)) {
      return;
    }
    if (remainingMs <= 0) {
      setQuoteExpired(true);
      return;
    }
    const timeoutId = window.setTimeout(() => setQuoteExpired(true), remainingMs);
    return () => window.clearTimeout(timeoutId);
  }, [quoteExpiresAt]);

  // The normalized Solana deposit instruction generated by the manual off-ramp provider.
  const cryptoDepositInstruction = useMemo(() => {
    const quote = wizard.quote;
    if (quote?.deliveryMode !== "manual_instructions") {
      return null;
    }
    return quote.paymentInstructions?.find(isCryptoDepositInstruction) ?? null;
  }, [wizard.quote]);

  const offrampCryptoToken = toRampCryptoToken(wizard.selectedRampPair.assetRail);
  // The transfers API requires the mint address, not the token symbol.
  const sourceTokenMint = useMemo(() => {
    const balance = wizard.selectedWallet?.balances?.find(
      (entry) => entry.token === offrampCryptoToken
    );
    return balance?.mint ?? null;
  }, [wizard.selectedWallet, offrampCryptoToken]);

  const amount = summaryAmount(wizard.fields.amount, locale);
  const summaryDetails: WizardSummaryDetail[] = [
    ...optionalDetail(
      wizard.selectedWallet === null ? null : wizard.selectedWallet.label,
      t("DashboardPayments.ramps.sourceWallet"),
      WalletIcon
    ),
    ...optionalDetail(
      amount === null ? null : `${amount} ${offrampCryptoToken}`,
      t("DashboardPayments.ramps.amount"),
      DollarSignIcon
    ),
    {
      icon: BanknoteIcon,
      label: t("DashboardPayments.payoutCurrency"),
      value: wizard.selectedRampPair.fiatCurrency,
    },
    ...providerSummaryDetail(t, wizard.fields.provider),
    ...memoSummaryDetails(t, wizard.memoRows),
  ];

  const hasCryptoDepositInstruction = cryptoDepositInstruction !== null;
  const canSendOnchain =
    hasCryptoDepositInstruction && sourceTokenMint !== null && wizard.fields.walletId.length > 0;

  const sendCryptoToDeposit = async () => {
    if (!cryptoDepositInstruction || !sourceTokenMint || !wizard.fields.walletId) {
      return;
    }
    if (onchainSendLoading || onchainSendResult) {
      return;
    }
    // Re-check the timestamp at call time — the armed timeout only covers renders.
    if (quoteExpiresAt && Date.parse(quoteExpiresAt) <= Date.now()) {
      setQuoteExpired(true);
      toast.error(t("DashboardPayments.ramps.quoteExpired"), {
        description: t("DashboardPayments.ramps.status.quoteExpiredPayoutDescription"),
        position: "bottom-right",
      });
      return;
    }

    setOnchainSendLoading(true);
    const toastId = toast.loading(t("DashboardPayments.ramps.submittingOnchainTransfer"), {
      position: "bottom-right",
    });

    try {
      const transfer = await createTransfer(
        {
          source: wizard.fields.walletId,
          destination: cryptoDepositInstruction.destinationAddress,
          token: sourceTokenMint,
          amount: wizard.fields.amount.trim(),
        },
        t
      );
      setOnchainSendResult(transfer);
      toast.success(t("DashboardPayments.ramps.transferSubmitted"), {
        id: toastId,
        description: transfer.signature
          ? t("DashboardPayments.ramps.transactionSentSuccessfully")
          : t("DashboardPayments.ramps.transferStatus", { status: transfer.status }),
        position: "bottom-right",
      });
    } catch (error) {
      toast.error(t("DashboardPayments.ramps.transferFailed"), {
        id: toastId,
        description:
          error instanceof Error ? error.message : t("DashboardPayments.ramps.transferFailed"),
        position: "bottom-right",
      });
    } finally {
      setOnchainSendLoading(false);
    }
  };

  const transferStatusKey = wizard.quote
    ? (["offramp-transfer-status", wizard.quote.provider, wizard.quote.id] as const)
    : null;
  const { data: transferStatus, isValidating: transferStatusLoading } = useSWR(
    transferStatusKey,
    ([, provider, providerReference]): Promise<PaymentTransferSummary | null> =>
      fetchTransferByProviderReference({ provider, providerReference }, t),
    {
      refreshInterval: (transfer) =>
        transfer && isTerminalRampTransferStatus(transfer.status) ? 0 : 3000,
      revalidateOnFocus: true,
      dedupingInterval: 0,
    }
  );

  return {
    ...wizard,
    summaryDetails,
    transferStatus,
    transferStatusLoading,
    sourceTokenMint,
    hasCryptoDepositInstruction,
    canSendOnchain,
    onchainSendLoading,
    onchainSendResult,
    sendCryptoToDeposit,
    quoteExpired,
  };
}

export type OfframpWizard = ReturnType<typeof useOfframpWizard>;
