import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Image, ScrollView, View } from 'react-native';
import { ActivityIndicator, Button, Text, Appbar } from 'react-native-paper';
import { providers, BigNumber } from 'ethers';

import { useNavigation } from '@react-navigation/native';
import {
  NativeStackNavigationProp,
  NativeStackScreenProps,
} from '@react-navigation/native-stack';
import { getHeaderTitle } from '@react-navigation/elements';
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
import {
  calculateFee,
  GasPrice,
  SigningStargateClient,
} from '@cosmjs/stargate';

import { Account, StackParamsList } from '../types';
import AccountDetails from '../components/AccountDetails';
import styles from '../styles/stylesheet';
import { retrieveSingleAccount } from '../utils/accounts';
import {
  approveWalletConnectRequest,
  rejectWalletConnectRequest,
} from '../utils/wallet-connect/WalletConnectRequests';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { EIP155_CHAINS } from '../utils/wallet-connect/EIP155Data';
import DataBox from '../components/DataBox';
import { getPathKey } from '../utils/misc';
import { COSMOS_TESTNET_CHAINS } from '../utils/wallet-connect/COSMOSData';
import { COSMOS_DENOM } from '../utils/constants';

type SignRequestProps = NativeStackScreenProps<
  StackParamsList,
  'ApproveTransaction'
>;

const ApproveTransaction = ({ route }: SignRequestProps) => {
  const requestSession = route.params.requestSessionData;
  const requestName = requestSession.peer.metadata.name;
  const requestIcon = requestSession.peer.metadata.icons[0];
  const requestURL = requestSession.peer.metadata.url;
  const network = route.params.network;
  const transaction = route.params.transaction;
  const requestEvent = route.params.requestEvent;
  const chainId = requestEvent.params.chainId;

  const [account, setAccount] = useState<Account>();
  const [isLoading, setIsLoading] = useState(true);
  const [balance, setBalance] = useState<string>('');
  const [isTxLoading, setIsTxLoading] = useState(false);
  const [cosmosStargateClient, setCosmosStargateClient] =
    useState<SigningStargateClient>();

  const provider = useMemo(() => {
    if (network === 'eth') {
      return new providers.JsonRpcProvider(EIP155_CHAINS[chainId].rpc);
    }
  }, [chainId, network]);

  const navigation =
    useNavigation<NativeStackNavigationProp<StackParamsList>>();

  const retrieveData = useCallback(
    async (requestAddress: string) => {
      const requestAccount = await retrieveSingleAccount(
        network,
        requestAddress,
      );
      if (!requestAccount) {
        navigation.navigate('InvalidPath');
        return;
      }

      setAccount(requestAccount);
      setIsLoading(false);
    },
    [navigation, network],
  );

  const gasFees = useMemo(() => {
    if (network === 'eth') {
      return BigNumber.from(transaction.gasLimit)
        .mul(BigNumber.from(transaction.gasPrice))
        .toString();
    } else {
      const gasPrice = GasPrice.fromString(transaction.gasPrice!.toString());
      const cosmosFees = calculateFee(Number(transaction.gasLimit), gasPrice);

      return cosmosFees.amount[0].amount;
    }
  }, [transaction, network]);

  useEffect(() => {
    retrieveData(transaction.from!);
  }, [retrieveData, transaction]);

  const acceptRequestHandler = async () => {
    setIsTxLoading(true);
    if (!account) {
      throw new Error('account not found');
    }

    const response = await approveWalletConnectRequest(
      requestEvent,
      account,
      network,
      '',
      network === 'eth' ? provider : cosmosStargateClient,
    );

    const { topic } = requestEvent;
    await web3wallet!.respondSessionRequest({ topic, response });
    setIsTxLoading(false);

    navigation.navigate('Laconic');
  };

  const rejectRequestHandler = async () => {
    const response = rejectWalletConnectRequest(requestEvent);
    const { topic } = requestEvent;
    await web3wallet!.respondSessionRequest({
      topic,
      response,
    });

    navigation.navigate('Laconic');
  };

  useEffect(() => {
    const getAccountBalance = async (account: Account) => {
      if (network === 'eth') {
        const fetchedBalance =
          provider && (await provider.getBalance(account.address));

        setBalance(fetchedBalance ? fetchedBalance.toString() : '0');
      } else {
        const cosmosBalance = await cosmosStargateClient?.getBalance(
          account.address,
          COSMOS_DENOM,
        );

        setBalance(cosmosBalance?.amount!);
      }
    };

    if (account) {
      getAccountBalance(account);
    }
  }, [account, provider, network, cosmosStargateClient]);

  useEffect(() => {
    navigation.setOptions({
      // eslint-disable-next-line react/no-unstable-nested-components
      header: ({ options, back }) => {
        const title = getHeaderTitle(options, 'Approve Transaction');

        return (
          <Appbar.Header>
            {back && (
              <Appbar.BackAction
                onPress={async () => {
                  await rejectRequestHandler();
                  navigation.navigate('Laconic');
                }}
              />
            )}
            <Appbar.Content title={title} />
          </Appbar.Header>
        );
      },
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [navigation, route.name]);

  useEffect(() => {
    if (network !== 'cosmos') {
      return;
    }

    const setClient = async () => {
      if (!account) {
        return;
      }

      const cosmosPrivKey = (await getPathKey('cosmos', account.counterId))
        .privKey;

      const sender = await DirectSecp256k1Wallet.fromKey(
        Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
      );

      const client = await SigningStargateClient.connectWithSigner(
        COSMOS_TESTNET_CHAINS[chainId as string].rpc,
        sender,
      );

      setCosmosStargateClient(client);
    };

    setClient();
  }, [account, chainId, network]);

  return (
    <>
      {isLoading ? (
        <View style={styles.spinnerContainer}>
          <ActivityIndicator size="large" color="#0000ff" />
        </View>
      ) : (
        <ScrollView contentContainerStyle={styles.appContainer}>
          <View style={styles.dappDetails}>
            {requestIcon && (
              <Image
                style={styles.dappLogo}
                source={requestIcon ? { uri: requestIcon } : undefined}
              />
            )}
            <Text>{requestName}</Text>
            <Text variant="bodyMedium">{requestURL}</Text>
          </View>
          <View style={styles.dataBoxContainer}>
            <Text style={styles.dataBoxLabel}>From</Text>
            <View style={styles.dataBox}>
              <AccountDetails account={account} />
            </View>
          </View>
          <DataBox
            label={`Balance ${
              network === 'eth' ? '(Wei)' : `(${COSMOS_DENOM})`
            }`}
            data={
              balance === '' || balance === undefined
                ? 'Loading balance...'
                : `${balance}`
            }
          />
          {transaction && (
            <View style={styles.approveTransaction}>
              <DataBox label="To" data={transaction.to!} />
              <DataBox
                label={`Amount ${
                  network === 'eth' ? '(Wei)' : `(${COSMOS_DENOM})`
                }`}
                data={BigNumber.from(transaction.value?.toString()).toString()}
              />
              <DataBox
                label={`Gas Fees ${
                  network === 'eth' ? '(Wei)' : `(${COSMOS_DENOM})`
                }`}
                data={gasFees!}
              />
              {network === 'eth' && (
                <DataBox label="Data" data={transaction.data!} />
              )}
            </View>
          )}
          <View style={styles.buttonContainer}>
            <Button
              mode="contained"
              onPress={acceptRequestHandler}
              loading={isTxLoading}
              disabled={!balance && !cosmosStargateClient}>
              {isTxLoading ? 'Processing' : 'Yes'}
            </Button>
            <Button
              mode="contained"
              onPress={rejectRequestHandler}
              buttonColor="#B82B0D">
              No
            </Button>
          </View>
        </ScrollView>
      )}
    </>
  );
};

export default ApproveTransaction;
