﻿import { FileInput } from '@/components/common/image-drop';
import PageHeading from '@/components/common/page-heading';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Separator } from '@/components/ui/separator';
import { createRequest } from '@/data/request/create-request';
import { useCurrency } from '@/hooks/use-currency';
import { request } from '@/lib/fetcher';
import { useDepositStore } from '@/store/useDepositStore';
import { SharedData } from '@/types';
import {
  TMetamaskConfirmStatus,
  TWeb3DepositAction,
} from '@/types/deposit.types';
import validateGatewayDetails from '@/utils/gatewayValidation';
import { getImageUrl } from '@/utils/getImageUrl';
import { calculateFees, formatFee } from '@/utils/handleFee';
import {
  isMetaMaskAvailable,
  metaMaskErrorMessage,
  payWithMetaMask,
} from '@/utils/metamask';
import { zodResolver } from '@hookform/resolvers/zod';
import { router, usePage } from '@inertiajs/react';
import {
  ArrowCounterClockwiseIcon,
  CaretLeftIcon,
  CaretRightIcon,
  InfoIcon,
  WarningIcon,
} from '@phosphor-icons/react';
import { useTransition } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';
import z from 'zod';

const ConfirmSchema = z.object({
  amount: z.coerce.number().min(1, 'Amount is required'),
  details: z.any().optional(),
});

export type TConfirmSchema = z.infer<typeof ConfirmSchema>;

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

/**
 * Poll the backend to verify a MetaMask deposit as it gains confirmations.
 * The endpoint is idempotent, so re-submitting the same hash is safe.
 */
async function pollMetamaskConfirm(
  trxId: string,
  txHash: string,
): Promise<TMetamaskConfirmStatus> {
  for (let attempt = 0; attempt < 20; attempt++) {
    try {
      const { data } = await request.post(
        '/api/user/deposits/metamask/confirm',
        { trx_id: trxId, tx_hash: txHash },
      );
      if (data?.status === 'completed') return 'completed';
      // pending -> keep waiting for confirmations
    } catch (err) {
      const status = (err as { response?: { data?: { status?: string } } })
        ?.response?.data?.status;
      if (status === 'failed') return 'failed';
      // transient network/RPC error -> retry
    }
    await sleep(4000);
  }
  return 'pending';
}

const DepositConfirm = () => {
  const { t } = useTranslation();
  const [isPending, startTransition] = useTransition();
  const { gateway } = useDepositStore();
  const { branding } = usePage<SharedData>().props;

  const form = useForm({
    resolver: zodResolver(ConfirmSchema),
    defaultValues: { amount: '', details: {} },
  });

  // eslint-disable-next-line react-hooks/incompatible-library
  const amount = form.watch('amount');
  const numericAmount = Number(amount || 0);

  const fee = gateway ? calculateFees(numericAmount, gateway) : null;

  const handleMetaMaskDeposit = async (action: TWeb3DepositAction) => {
    if (!isMetaMaskAvailable()) {
      toast.error(
        t('MetaMask was not detected. Please install the MetaMask extension.'),
      );
      return;
    }

    try {
      toast.info(t('Confirm the transaction in MetaMask…'));
      const txHash = await payWithMetaMask(action);

      toast.info(t('Transaction sent. Waiting for confirmations…'));
      const status = await pollMetamaskConfirm(action.trx_id, txHash);

      if (status === 'completed') {
        toast.success(t('Deposit credited successfully.'));
        router.visit('/deposit/success');
      } else if (status === 'failed') {
        toast.error(
          t(
            'On-chain verification failed. Please check the amount and network.',
          ),
        );
      } else {
        toast.info(
          t(
            'Payment received and is awaiting confirmations. Check your transaction history shortly.',
          ),
        );
        router.visit('/deposit/success');
      }
    } catch (err) {
      toast.error(metaMaskErrorMessage(err));
    }
  };

  const handleSubmit = (values: TConfirmSchema) => {
    const min = gateway?.min_amount ?? 0;
    const max = gateway?.max_amount ?? Infinity;

    if (gateway && (values.amount < min || values.amount > max)) {
      form.setError('amount', {
        message: `Amount must be between ${min.toLocaleString()} and ${max.toLocaleString()} ${gateway.currency}`,
      });
      return;
    }

    if (gateway?.gateway_type === 'manual') {
      const firstInvalid = validateGatewayDetails(
        gateway.fields,
        values.details,
        form.setError,
      );

      if (firstInvalid) {
        form.setFocus(firstInvalid as never);
        return;
      }
    }

    startTransition(async () => {
      const isManual = gateway?.gateway_type === 'manual';

      const formData = isManual
        ? {
            manual_method_id: gateway?.id,
            amount: values.amount,
            details: values.details ?? {},
          }
        : {
            auto_method_id: gateway?.id,
            amount: values.amount,
          };

      const res = await createRequest({
        url: isManual ? '/api/user/deposits/manual' : '/api/user/deposits/auto',
        formData,
      });

      // Self-custody wallet gateways (MetaMask) settle in the browser.
      if (res?.action === 'web3') {
        await handleMetaMaskDeposit(res as TWeb3DepositAction);
        return;
      }

      // Hosted-checkout gateways return a URL to send the user to.
      if (res?.action === 'redirect' && res?.url) {
        window.location.href = res.url;
        return;
      }

      if (res?.transaction_id) {
        toast.success(res?.message);
        router.visit('/deposit/success');
      }
    });
  };

  return (
    <>
      <PageHeading
        variant="default"
        title={t('Deposit Confirm')}
        description={t('Review and confirm your deposit details.')}
      />

      <Card>
        <h3 className="py-4 text-center text-2xl font-medium">
          {t('Confirm Deposit')}
        </h3>
        <Separator className="my-4 bg-black/5 dark:bg-white/5" />

        <Form {...form}>
          <form onSubmit={form.handleSubmit(handleSubmit)}>
            {gateway === null ? (
              <div className="flex flex-col items-center py-10 text-center text-muted-foreground">
                <WarningIcon
                  weight="duotone"
                  className="mb-2 size-10 text-foreground dark:text-white/60"
                />
                <p className="mb-4">{t('No payment gateways selected.')}</p>
                <Button
                  type="button"
                  variant="secondary"
                  onClick={() => router.visit('/deposit')}
                  className="min-w-48 rounded-full"
                >
                  <ArrowCounterClockwiseIcon weight="bold" />
                  <span>{t('Select Gateway')}</span>
                </Button>
              </div>
            ) : (
              <div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
                <div className="space-y-4">
                  {gateway?.instructions && (
                    <div className="rounded-2xl border border-black/5 bg-white/10 px-4 py-2.5">
                      <div className="mb-2 flex items-center gap-2">
                        <InfoIcon
                          weight="fill"
                          className="size-5 text-black/70 dark:text-white/50"
                        />
                        <p>{t('Instructions')}</p>
                      </div>
                      <p className="font-medium text-black/70 opacity-50 dark:text-white/50">
                        {gateway?.instructions}
                      </p>
                    </div>
                  )}
                  <FormField
                    control={form.control}
                    name="amount"
                    render={({ field }) => (
                      <FormItem>
                        <FormLabel className="flex justify-between px-2 font-lexend text-black/50 dark:text-white/50">
                          <span>{t('Amount')}</span>
                          <span>
                            {gateway?.min_amount != null &&
                              gateway?.max_amount != null &&
                              `${t('Limit:')} ${gateway.min_amount.toLocaleString()}-${gateway.max_amount.toLocaleString()} ${gateway.currency}`}
                          </span>
                        </FormLabel>
                        <FormControl>
                          <Input
                            type="number"
                            inputMode="decimal"
                            placeholder={t('Enter amount')}
                            className="rounded-xl border border-black/5 bg-black/5 dark:border-white/5 dark:bg-secondary"
                            {...field}
                            value={field.value as string | number}
                          />
                        </FormControl>
                        <FormMessage className="px-4" />
                      </FormItem>
                    )}
                  />

                  {gateway?.fields?.map((f) =>
                    f.type === 'image' ? (
                      <FormField
                        key={f.name}
                        control={form.control}
                        name={`details.${f.name}`}
                        render={({ field }) => (
                          <FormItem>
                            <FormLabel className="px-4 font-lexend text-black/50 dark:text-white/50">
                              {f.name}
                            </FormLabel>
                            <FormControl>
                              <FileInput
                                id={f.name}
                                className="rounded-2xl border border-black/5 bg-black/5 dark:border-white/5 dark:bg-secondary"
                                defaultValue={
                                  typeof field.value === 'string'
                                    ? getImageUrl(field.value)
                                    : undefined
                                }
                                onChange={(file) => field.onChange(file)}
                              />
                            </FormControl>
                            <FormMessage className="px-4" />
                          </FormItem>
                        )}
                      />
                    ) : (
                      <FormField
                        key={f.name}
                        control={form.control}
                        name={`details.${f.name}`}
                        render={({ field }) => (
                          <FormItem>
                            <FormLabel className="px-4 font-lexend text-black/50 dark:text-white/50">
                              {f.name}
                            </FormLabel>
                            <FormControl>
                              <Input
                                type={f.type}
                                inputMode={
                                  f.type === 'number' ? 'numeric' : 'text'
                                }
                                placeholder={f.placeholder}
                                className="rounded-xl border border-black/5 bg-black/5 dark:border-white/5 dark:bg-secondary"
                                {...field}
                                value={field.value as string | number}
                              />
                            </FormControl>
                            <FormMessage className="px-4" />
                          </FormItem>
                        )}
                      />
                    ),
                  )}
                </div>

                <div className="space-y-4 font-lexend">
                  <div className="flex items-center justify-between border-b border-dashed border-white/15">
                    <p>{t('Currency')}</p>
                    <p>{gateway?.currency}</p>
                  </div>
                  <div className="flex items-center justify-between border-b border-dashed border-white/15">
                    <p>{t('Method')}</p>
                    <p>{gateway?.name}</p>
                  </div>
                  <div className="flex items-center justify-between border-b border-dashed border-white/15">
                    <p>{t('Processing Time')}</p>
                    <p>
                      {gateway?.gateway_type === 'auto'
                        ? t('Instant')
                        : t('Manual')}
                    </p>
                  </div>
                  <div className="flex items-center justify-between border-b border-dashed border-white/15">
                    <p>{t('Fee')}</p>
                    <p>
                      {formatFee({
                        fixed_charge: gateway?.fixed_charge,
                        percent_charge: gateway?.percent_charge,
                        currency: gateway?.currency,
                      })}
                    </p>
                  </div>
                  <div className="flex items-center justify-between border-b border-dashed border-white/15">
                    <p>{t('Deposit amount')}</p>
                    <p>
                      <Money
                        value={numericAmount + (fee?.totalFee || 0)}
                        currency={gateway?.currency}
                      />
                    </p>
                  </div>
                  <div className="flex items-center justify-between border-b border-dashed border-white/15">
                    <p>{t('Adjusted fee')}</p>
                    <p>
                      <span className="text-2xl font-medium">-</span>
                      <Money
                        value={fee?.totalFee}
                        currency={gateway?.currency}
                      />
                    </p>
                  </div>
                  {branding?.branding?.site_currency !== gateway?.currency && (
                    <>
                      <div className="flex items-center justify-between border-b border-dashed border-white/15">
                        <p>{t('Adjusted amount')}</p>
                        <p>
                          <Money
                            value={numericAmount}
                            currency={gateway?.currency}
                          />
                        </p>
                      </div>
                    </>
                  )}
                  <div className="flex items-center justify-between border-b border-dashed border-white/15">
                    <p>{t('Receiving amount')}</p>
                    <p>
                      <Money
                        value={
                          (numericAmount || 0) * (gateway?.exchange_rate || 1)
                        }
                        currency={branding?.branding?.site_currency}
                      />
                    </p>
                  </div>
                </div>
              </div>
            )}

            <Separator className="my-4 bg-black/5 dark:bg-white/5" />

            <div className="flex justify-between">
              <Button
                type="button"
                variant="secondary"
                onClick={() => router.visit('/deposit')}
                className="min-w-32"
              >
                <CaretLeftIcon weight="bold" />
                {t('Back')}
              </Button>
              <Button
                type="submit"
                loading={isPending}
                variant="primary"
                className="min-w-32"
              >
                <span>{t('Continue')}</span>
                <CaretRightIcon weight="bold" />
              </Button>
            </div>
          </form>
        </Form>
      </Card>
    </>
  );
};

const Money = ({ value, currency }: { value?: number; currency?: string }) => {
  const { format } = useCurrency();

  const amount = format(value || 0, 'symbol', currency);

  const splitAmount = amount.includes('.') ? amount.split('.') : [amount, '00'];

  const [intPart, decPart] = splitAmount;

  return (
    <span className="font-lexend">
      <span className="text-2xl font-medium tabular-nums">{intPart}</span>
      <span className="tabular-nums">.{decPart}</span>
      <span className="ml-1 text-sm opacity-80">{currency}</span>
    </span>
  );
};

DepositConfirm.title = 'Deposit Confirm';

export default DepositConfirm;
