/**
 * @file BridgeButtons.tsx
 * @notice Custom bridge UI with Wrap, Approve, and Bridge buttons using thirdweb
 */

import {
  useContract,
  useContractWrite,
  useContractRead,
  useAddress,
  useBalance,
} from '@thirdweb-dev/react';
import { useState, useEffect } from 'react';
import { ethers } from 'ethers';
import toast from 'react-hot-toast';
import {
  CONTRACTS,
  CHAIN_SELECTORS,
  BRIDGE_ABI,
  WETH9_ABI,
  ERC20_ABI,
  CCIP_DESTINATIONS,
} from '../../config/bridge';
import CopyButton from '../ui/CopyButton';
import ConfirmationModal from '../ui/ConfirmationModal';
import Tooltip from '../ui/Tooltip';
import LoadingSkeleton from '../ui/LoadingSkeleton';
import TokenIcon from '../ui/TokenIcon';

interface BridgeButtonsProps {
  destinationChainSelector?: string; // Defaults to Ethereum Mainnet
  recipientAddress?: string; // Defaults to connected wallet
}

function getErrorMessage(error: unknown): string {
  if (typeof error === 'object' && error !== null) {
    const maybeMessage = 'message' in error ? error.message : undefined
    const maybeReason = 'reason' in error ? error.reason : undefined
    if (typeof maybeMessage === 'string' && maybeMessage.length > 0) return maybeMessage
    if (typeof maybeReason === 'string' && maybeReason.length > 0) return maybeReason
  }
  return 'Unknown error'
}

/** Fetches CCIP fee only when amount > 0 (bridge reverts on zero). Reports result to parent via onFee. */
function CalculateFeeFetcher({
  bridgeContract,
  destinationChainSelector,
  amountWei,
  onFee,
}: {
  bridgeContract: NonNullable<ReturnType<typeof useContract>['contract']>;
  destinationChainSelector: string;
  amountWei: ethers.BigNumber;
  onFee: (value: ethers.BigNumber | null) => void;
}) {
  const { data } = useContractRead(bridgeContract, 'calculateFee', [
    destinationChainSelector,
    amountWei,
  ]);
  useEffect(() => {
    if (data != null) onFee(ethers.BigNumber.from(data.toString()));
    return () => {};
  }, [data, onFee]);
  return null;
}

/** Inner bridge form: only mounted when address is set so balance/allowance are never called with zero address. */
function BridgeButtonsConnected({
  address,
  destinationChainSelector = CHAIN_SELECTORS.ETHEREUM_MAINNET,
  recipientAddress,
}: BridgeButtonsProps & { address: string }) {
  const [amount, setAmount] = useState<string>('');
  const [recipient, setRecipient] = useState<string>(recipientAddress || address || '');
  const [isWrapping, setIsWrapping] = useState(false);
  const [isApproving, setIsApproving] = useState(false);
  const [isBridging, setIsBridging] = useState(false);
  const [showWrapModal, setShowWrapModal] = useState(false);
  const [showApproveModal, setShowApproveModal] = useState(false);
  const [showBridgeModal, setShowBridgeModal] = useState(false);
  const [amountError, setAmountError] = useState<string>('');
  const [recipientError, setRecipientError] = useState<string>('');
  const [ccipFee, setCcipFee] = useState<ethers.BigNumber | null>(null);

  const { contract: weth9Contract } = useContract(CONTRACTS.WETH9, WETH9_ABI);
  const { contract: bridgeContract } = useContract(CONTRACTS.WETH9_BRIDGE, BRIDGE_ABI);
  const { contract: linkContract } = useContract(CONTRACTS.LINK_TOKEN, ERC20_ABI);

  const { data: ethBalance, refetch: refetchEthBalance } = useBalance();
  const { data: weth9Balance, refetch: refetchWeth9Balance } = useContractRead(
    weth9Contract,
    'balanceOf',
    weth9Contract ? [address] : undefined
  );
  const { data: linkBalance, refetch: refetchLinkBalance } = useContractRead(
    linkContract,
    'balanceOf',
    linkContract ? [address] : undefined
  );
  const { data: weth9Allowance, refetch: refetchWeth9Allowance } = useContractRead(
    weth9Contract,
    'allowance',
    weth9Contract && bridgeContract ? [address, CONTRACTS.WETH9_BRIDGE] : undefined
  );
  const { data: linkAllowance, refetch: refetchLinkAllowance } = useContractRead(
    linkContract,
    'allowance',
    linkContract && bridgeContract ? [address, CONTRACTS.WETH9_BRIDGE] : undefined
  );

  const amountWei = amount && !isNaN(parseFloat(amount)) && parseFloat(amount) > 0
    ? ethers.utils.parseEther(amount)
    : ethers.BigNumber.from(0);

  useEffect(() => {
    if (!recipientAddress) setRecipient((r) => r || address);
  }, [address, recipientAddress]);

  useEffect(() => {
    if (!amount) {
      setAmountError('');
      return;
    }
    const numAmount = parseFloat(amount);
    if (isNaN(numAmount) || numAmount <= 0) {
      setAmountError('Amount must be greater than 0');
    } else if (ethBalance && numAmount > parseFloat(ethBalance.displayValue)) {
      setAmountError('Insufficient ETH balance');
    } else {
      setAmountError('');
    }
  }, [amount, ethBalance]);

  useEffect(() => {
    if (!recipient) {
      setRecipientError('');
      return;
    }
    setRecipientError(ethers.utils.isAddress(recipient) ? '' : 'Invalid Ethereum address');
  }, [recipient]);

  useEffect(() => {
    if (!amountWei.gt(0)) setCcipFee(null);
  }, [amountWei]);

  // Write operations
  const { mutateAsync: wrapETH } = useContractWrite(weth9Contract, 'deposit');
  const { mutateAsync: approveWETH9 } = useContractWrite(weth9Contract, 'approve');
  const { mutateAsync: approveLINK } = useContractWrite(linkContract, 'approve');
  const { mutateAsync: sendCrossChain } = useContractWrite(bridgeContract, 'sendCrossChain');

  const handleRefreshBalances = async () => {
    try {
      await Promise.all([
        refetchEthBalance().catch(() => {}),
        refetchWeth9Balance().catch(() => {}),
        refetchLinkBalance().catch(() => {}),
        refetchWeth9Allowance().catch(() => {}),
        refetchLinkAllowance().catch(() => {}),
      ]);
      toast.success('Balances refreshed');
    } catch (error) {
      // Silently handle errors - some refetches may fail if contracts aren't ready
      toast.success('Balances refreshed');
    }
  };

  const handleWrap = async () => {
    if (!address) {
      toast.error('Please connect your wallet');
      return;
    }

    if (!amount || parseFloat(amount) <= 0) {
      toast.error('Please enter an amount');
      return;
    }

    if (amountError) {
      toast.error(amountError);
      return;
    }

    if (!ethBalance || parseFloat(ethBalance.displayValue) < parseFloat(amount)) {
      toast.error('Insufficient ETH balance');
      return;
    }

    setIsWrapping(true);
    const toastId = toast.loading('Wrapping ETH...');
    try {
      const tx = await wrapETH({
        overrides: {
          value: ethers.utils.parseEther(amount),
        },
      });
      console.log('Wrap transaction:', tx);
      toast.success('ETH wrapped successfully!', { id: toastId });
      setAmount('');
      await handleRefreshBalances();
    } catch (error: unknown) {
      console.error('Wrap error:', error);
      toast.error(`Wrap failed: ${getErrorMessage(error)}`, { id: toastId });
    } finally {
      setIsWrapping(false);
    }
  };

  const handleApprove = async () => {
    if (!address) {
      toast.error('Please connect your wallet');
      return;
    }

    if (!amount || parseFloat(amount) <= 0) {
      toast.error('Please enter an amount');
      return;
    }

    setIsApproving(true);
    const toastId = toast.loading('Approving tokens...');
    try {
      const amountWei = ethers.utils.parseEther(amount);
      const maxApproval = ethers.constants.MaxUint256;

      // Approve WETH9
      const weth9AllowanceBN = weth9Allowance
        ? ethers.BigNumber.from(weth9Allowance.toString())
        : ethers.BigNumber.from(0);

      if (weth9AllowanceBN.lt(amountWei)) {
        const weth9Tx = await approveWETH9({
          args: [CONTRACTS.WETH9_BRIDGE, maxApproval],
        });
        console.log('WETH9 approval transaction:', weth9Tx);
        toast.success('WETH9 approved', { id: toastId });
      }

      // Approve LINK for fees (if needed)
      if (ccipFee?.gt(0)) {
        const linkAllowanceBN = linkAllowance
          ? ethers.BigNumber.from(linkAllowance.toString())
          : ethers.BigNumber.from(0);

        if (linkAllowanceBN.lt(ccipFee)) {
          const linkTx = await approveLINK({
            args: [CONTRACTS.WETH9_BRIDGE, maxApproval],
          });
          console.log('LINK approval transaction:', linkTx);
          toast.success('LINK approved', { id: toastId });
        }
      }

      toast.success('All approvals successful!', { id: toastId });
      await handleRefreshBalances();
    } catch (error: unknown) {
      console.error('Approve error:', error);
      toast.error(`Approval failed: ${getErrorMessage(error)}`, { id: toastId });
    } finally {
      setIsApproving(false);
    }
  };

  const handleBridge = async () => {
    if (!address) {
      toast.error('Please connect your wallet');
      return;
    }

    if (!amount || parseFloat(amount) <= 0) {
      toast.error('Please enter an amount');
      return;
    }

    if (amountError || recipientError) {
      toast.error('Please fix the errors before bridging');
      return;
    }

    if (!recipient || !ethers.utils.isAddress(recipient)) {
      toast.error('Please enter a valid recipient address');
      return;
    }

    const amountWei = ethers.utils.parseEther(amount);
    const weth9BalanceBN = weth9Balance
      ? ethers.BigNumber.from(weth9Balance.toString())
      : ethers.BigNumber.from(0);

    if (weth9BalanceBN.lt(amountWei)) {
      toast.error('Insufficient WETH9 balance. Please wrap ETH first.');
      return;
    }

    const weth9AllowanceBN = weth9Allowance
      ? ethers.BigNumber.from(weth9Allowance.toString())
      : ethers.BigNumber.from(0);

    if (weth9AllowanceBN.lt(amountWei)) {
      toast.error('Insufficient WETH9 allowance. Please approve first.');
      return;
    }

    if (ccipFee?.gt(0)) {
      const linkBalanceBN = linkBalance
        ? ethers.BigNumber.from(linkBalance.toString())
        : ethers.BigNumber.from(0);

      if (linkBalanceBN.lt(ccipFee)) {
        const feeFormatted = ethers.utils.formatEther(ccipFee);
        toast.error(`Insufficient LINK for fees. Required: ${feeFormatted} LINK`);
        return;
      }
    }

    setIsBridging(true);
    const toastId = toast.loading('Bridging tokens...');
    try {
      const result = await sendCrossChain({
        args: [
          destinationChainSelector, // destinationChainSelector
          recipient, // recipient
          amountWei, // amount
        ],
      });
      console.log('Bridge transaction:', result);
      const txHash = result.receipt?.transactionHash || 'N/A';
      toast.success(
        <div>
          <p className="font-semibold">Bridge transaction sent!</p>
          <p className="text-xs mt-1">TX: {txHash.slice(0, 10)}...{txHash.slice(-8)}</p>
        </div>,
        { id: toastId, duration: 6000 }
      );
      setAmount('');
      await handleRefreshBalances();
    } catch (error: unknown) {
      console.error('Bridge error:', error);
      toast.error(`Bridge failed: ${getErrorMessage(error)}`, { id: toastId });
    } finally {
      setIsBridging(false);
    }
  };

  // Button state calculations
  const needsWrapping =
    ethBalance &&
    amount &&
    parseFloat(amount) > 0 &&
    parseFloat(ethBalance.displayValue) >= parseFloat(amount) &&
    !amountError;

  const needsApproval =
    amount &&
    parseFloat(amount) > 0 &&
    weth9Allowance &&
    ethers.BigNumber.from(weth9Allowance.toString()).lt(ethers.utils.parseEther(amount)) &&
    !amountError;

  const canBridge =
    amount &&
    parseFloat(amount) > 0 &&
    !amountError &&
    !recipientError &&
    weth9Balance &&
    ethers.BigNumber.from(weth9Balance.toString()).gte(ethers.utils.parseEther(amount)) &&
    weth9Allowance &&
    ethers.BigNumber.from(weth9Allowance.toString()).gte(ethers.utils.parseEther(amount)) &&
    recipient &&
    ethers.utils.isAddress(recipient);

  const destination = CCIP_DESTINATIONS.find((d) => d.selector === destinationChainSelector) ?? null;

  return (
    <div className="w-full">
        {bridgeContract && amountWei.gt(0) && (
          <CalculateFeeFetcher
            bridgeContract={bridgeContract}
            destinationChainSelector={destinationChainSelector}
            amountWei={amountWei}
            onFee={setCcipFee}
          />
        )}
        <div className="relative">
        <div className="mb-6 flex items-center justify-between gap-3">
          <p className="ui-muted text-sm">
            Wrap ETH, approve tokens, and bridge WETH9 via CCIP to{' '}
            {destination?.name ?? 'Ethereum Mainnet'}
          </p>
          <Tooltip content="Refresh all balances and allowances">
            <button
              type="button"
              onClick={handleRefreshBalances}
              disabled={!address}
              className="ui-btn-icon disabled:opacity-50 disabled:cursor-not-allowed shrink-0"
              aria-label="Refresh balances"
            >
              <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
              </svg>
            </button>
          </Tooltip>
        </div>

        <div className="mb-8">
          <label className="ui-label">
            <TokenIcon symbol="ETH" size={20} />
            <span>Amount (ETH)</span>
            <Tooltip content="Enter the amount of ETH you want to bridge. This will be wrapped to WETH9 first.">
              <span className="text-white/60 cursor-help text-xs">ℹ️</span>
            </Tooltip>
          </label>
          <div className="relative">
            <input
              type="number"
              step="0.001"
              min="0"
              value={amount}
              onChange={(e) => setAmount(e.target.value)}
              className={`ui-input pl-4 pr-24 text-lg ${
                amountError ? '!border-red-500 focus:!border-red-500' : ''
              }`}
              placeholder="0.0"
              aria-invalid={!!amountError}
              aria-describedby={amountError ? 'amount-error' : undefined}
            />
            {ethBalance && (
              <button
                onClick={() => {
                  setAmount(ethBalance.displayValue);
                  setAmountError('');
                }}
                className="ui-btn-primary absolute right-2 top-1/2 -translate-y-1/2 !py-2 !min-h-0 text-sm"
              >
                MAX
              </button>
            )}
          </div>
          {amountError && (
            <p id="amount-error" className="mt-2 text-sm text-red-600 flex items-center gap-1">
              <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
              </svg>
              {amountError}
            </p>
          )}
        </div>

        <div className="mb-8">
          <label htmlFor="bridge-recipient-address" className="ui-label">
            Recipient Address
            <Tooltip content="The Ethereum address that will receive the bridged tokens on the destination chain.">
              <span className="ml-2 text-white/60 cursor-help text-xs">ℹ️</span>
            </Tooltip>
          </label>
          <div className="relative">
            <input
              id="bridge-recipient-address"
              name="recipient"
              type="text"
              value={recipient}
              onChange={(e) => setRecipient(e.target.value)}
              className={`ui-input font-mono pr-36 ${
                recipientError ? '!border-red-500 focus:!border-red-500' : ''
              }`}
              placeholder="0x..."
              aria-invalid={!!recipientError}
              aria-describedby={recipientError ? 'recipient-error' : undefined}
            />
            <div className="absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2">
              {address && (
                <button
                  onClick={() => {
                    setRecipient(address);
                    setRecipientError('');
                  }}
                  className="ui-btn-secondary !py-2 text-sm"
                >
                  Use my address
                </button>
              )}
              {recipient && ethers.utils.isAddress(recipient) && (
                <CopyButton text={recipient} />
              )}
            </div>
          </div>
          {recipientError && (
            <p id="recipient-error" className="mt-2 text-sm text-red-600 flex items-center gap-1">
              <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
              </svg>
              {recipientError}
            </p>
          )}
        </div>

        <div className="mb-8 p-6 ui-inset rounded-xl">
          <div className="flex items-center justify-between mb-4">
            <h3 className="text-lg font-semibold ui-muted">Balances & Fees</h3>
            {address && (
              <CopyButton text={address} className="text-xs">
                <span className="text-xs">Copy Address</span>
              </CopyButton>
            )}
          </div>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
            <div className="ui-stat-row">
              <span className="ui-muted font-medium flex items-center gap-2">
                <TokenIcon symbol="ETH" size={20} />
                ETH Balance:
              </span>
              <span className="font-medium">
                {ethBalance ? ethBalance.displayValue : <LoadingSkeleton />} <span className="ui-muted">ETH</span>
              </span>
            </div>
            <div className="ui-stat-row">
              <span className="ui-muted font-medium flex items-center gap-2">
                <TokenIcon symbol="WETH9" size={20} />
                WETH9 Balance:
              </span>
              <span className="font-medium">
                {address && weth9Balance !== undefined
                  ? `${ethers.utils.formatEther(weth9Balance.toString())}`
                  : address
                  ? <LoadingSkeleton />
                  : '0'} <span className="ui-muted">WETH9</span>
              </span>
            </div>
            <div className="ui-stat-row">
              <span className="ui-muted font-medium flex items-center gap-2">
                <TokenIcon symbol="LINK" size={20} />
                LINK Balance:
              </span>
              <span className="font-medium">
                {address && linkBalance !== undefined
                  ? `${ethers.utils.formatEther(linkBalance.toString())}`
                  : address
                  ? <LoadingSkeleton />
                  : '0'} <span className="ui-muted">LINK</span>
              </span>
            </div>
            {ccipFee != null && ccipFee.gt(0) && (
              <div className="ui-stat-row ui-stat-row--highlight">
                <span className="ui-muted font-medium flex items-center gap-2">
                  <TokenIcon symbol="LINK" size={20} />
                  CCIP Fee:
                </span>
                <span className="font-medium">
                  {ethers.utils.formatEther(ccipFee)} <span className="ui-muted">LINK</span>
                </span>
              </div>
            )}
          </div>
        </div>

        <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
          <button
            onClick={() => setShowWrapModal(true)}
            disabled={!needsWrapping || !address || isWrapping}
            className="ui-btn-secondary w-full"
            aria-label="Wrap ETH to WETH9"
          >
            {isWrapping ? (
              <span className="flex items-center justify-center gap-2">
                <svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
                  <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
                  <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                </svg>
                Wrapping...
              </span>
            ) : (
              'Wrap (Deposit)'
            )}
          </button>

          <button
            onClick={() => setShowApproveModal(true)}
            disabled={!needsApproval || !address || isApproving}
            className="ui-btn-secondary w-full"
            aria-label="Approve tokens for bridge"
          >
            {isApproving ? (
              <span className="flex items-center justify-center gap-2">
                <svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
                  <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
                  <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                </svg>
                Approving...
              </span>
            ) : (
              'Approve'
            )}
          </button>
        </div>
        <div className="mb-8">
          <button
            onClick={() => setShowBridgeModal(true)}
            disabled={!canBridge || !address || isBridging}
            className="ui-btn-primary w-full min-h-[56px] text-lg"
            aria-label="Start bridge transfer"
          >
            {isBridging ? (
              <span className="flex items-center justify-center gap-2">
                <svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
                  <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
                  <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                </svg>
                Bridging...
              </span>
            ) : (
              'Start Bridge Transfer'
            )}
          </button>
        </div>

        {/* Confirmation Modals */}
        <ConfirmationModal
          isOpen={showWrapModal}
          onClose={() => setShowWrapModal(false)}
          onConfirm={() => {
            setShowWrapModal(false);
            handleWrap();
          }}
          title="Wrap ETH to WETH9"
          message={`You are about to wrap ${amount} ETH to WETH9. This action cannot be undone.`}
          confirmText="Wrap ETH"
          confirmColor="blue"
          isLoading={isWrapping}
        />

        <ConfirmationModal
          isOpen={showApproveModal}
          onClose={() => setShowApproveModal(false)}
          onConfirm={() => {
            setShowApproveModal(false);
            handleApprove();
          }}
          title="Approve Tokens"
          message={`You are about to approve ${amount} WETH9 and required LINK tokens for the bridge contract. This will allow the bridge to transfer your tokens.`}
          confirmText="Approve"
          confirmColor="green"
          isLoading={isApproving}
        />

        <ConfirmationModal
          isOpen={showBridgeModal}
          onClose={() => setShowBridgeModal(false)}
          onConfirm={() => {
            setShowBridgeModal(false);
            handleBridge();
          }}
          title="Bridge Tokens"
          message={`You are about to bridge ${amount} WETH9 to ${recipient.slice(0, 6)}...${recipient.slice(-4)} on Ethereum Mainnet. This action cannot be undone.`}
          confirmText="Bridge"
          confirmColor="purple"
          isLoading={isBridging}
        />
        </div>
    </div>
  );
}

export default function BridgeButtons(props: BridgeButtonsProps) {
  const address = useAddress();
  if (!address) {
    const destinationChainSelector = props.destinationChainSelector ?? CHAIN_SELECTORS.ETHEREUM_MAINNET;
    const destination = CCIP_DESTINATIONS.find((d) => d.selector === destinationChainSelector);
    return (
      <div className="w-full">
        <div className="ui-alert">
          <svg className="h-5 w-5 shrink-0 ui-accent-text" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
          </svg>
          <span>Connect your wallet to bridge to {destination?.name ?? 'Ethereum Mainnet'}</span>
        </div>
      </div>
    );
  }
  return <BridgeButtonsConnected {...props} address={address} />;
}
