import React, { useEffect, useState } from 'react';
import { ScrollView, TouchableOpacity, View } from 'react-native';
import { Button, List, Text, useTheme } from 'react-native-paper';
import mergeWith from 'lodash/mergeWith';

import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';

import { AccountsProps, StackParamsList, Account } from '../types';
import { addAccount } from '../utils/accounts';
import styles from '../styles/stylesheet';
import HDPathDialog from './HDPathDialog';
import AccountDetails from './AccountDetails';
import { useAccounts } from '../context/AccountsContext';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
import { useNetworks } from '../context/NetworksContext';
import { COSMOS, EIP155 } from '../utils/constants';
import { NETWORK_METHODS } from '../utils/wallet-connect/common-data';

const Accounts = ({ currentIndex, updateIndex }: AccountsProps) => {
  const navigation =
    useNavigation<NativeStackNavigationProp<StackParamsList>>();

  const { accounts, setAccounts } = useAccounts();
  const { selectedNetwork } = useNetworks();
  const [expanded, setExpanded] = useState(false);
  const [isAccountCreating, setIsAccountCreating] = useState(false);
  const [hdDialog, setHdDialog] = useState(false);
  const [pathCode, setPathCode] = useState('');
  const theme = useTheme();

  const handlePress = () => setExpanded(!expanded);

  const updateAccounts = (account: Account) => {
    setAccounts([...accounts, account]);
  };

  useEffect(() => {
    const updateSessions = async () => {
      const sessions = (web3wallet && web3wallet.getActiveSessions()) || {};
      // Iterate through each session

      for (const topic in sessions) {
        const session = sessions[topic];
        const combinedNamespaces = mergeWith(
          session.requiredNamespaces,
          session.optionalNamespaces,
          (obj, src) =>
            Array.isArray(obj) && Array.isArray(src)
              ? [...src, ...obj]
              : undefined,
        );

        const namespaceChainId = `${selectedNetwork?.namespace}:${selectedNetwork?.chainId}`;

        let updatedNamespaces;
        switch (selectedNetwork?.namespace) {
          case EIP155:
            updatedNamespaces = {
              eip155: {
                chains: [namespaceChainId],
                // TODO: Debug optional namespace methods and events being required for approval
                methods: [
                  ...Object.values(EIP155_SIGNING_METHODS),
                  ...Object.values(NETWORK_METHODS),
                  ...(combinedNamespaces.eip155?.methods ?? []),
                ],
                events: [...(combinedNamespaces.eip155?.events ?? [])],
                accounts: accounts.map(ethAccount => {
                  return `${namespaceChainId}:${ethAccount.address}`;
                }),
              },
              cosmos: {
                chains: [],
                methods: [],
                events: [],
                accounts: [],
              },
            };
            break;
          case COSMOS:
            updatedNamespaces = {
              cosmos: {
                chains: [namespaceChainId],
                methods: [
                  ...Object.values(COSMOS_METHODS),
                  ...Object.values(NETWORK_METHODS),
                  ...(combinedNamespaces.cosmos?.methods ?? []),
                ],
                events: [...(combinedNamespaces.cosmos?.events ?? [])],
                accounts: accounts.map(cosmosAccount => {
                  return `${namespaceChainId}:${cosmosAccount.address}`;
                }),
              },
              eip155: {
                chains: [],
                methods: [],
                events: [],
                accounts: [],
              },
            };
            break;
          default:
            break;
        }

        if (!updatedNamespaces) {
          return;
        }

        await web3wallet!.updateSession({
          topic,
          namespaces: updatedNamespaces,
        });
      }
    };
    // Call the updateSessions function when the 'accounts' dependency changes
    updateSessions();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [accounts]);

  const addAccountHandler = async () => {
    setIsAccountCreating(true);
    const newAccount = await addAccount(selectedNetwork!);
    setIsAccountCreating(false);
    if (newAccount) {
      updateAccounts(newAccount);
      updateIndex(newAccount.index);
    }
  };

  const renderAccountItems = () =>
    accounts.map(account => (
      <List.Item
        key={account.index}
        title={`Account ${account.index + 1}`}
        onPress={() => {
          updateIndex(account.index);
          setExpanded(false);
        }}
      />
    ));

  return (
    <ScrollView>
      <View>
        <HDPathDialog
          visible={hdDialog}
          hideDialog={() => setHdDialog(false)}
          updateAccounts={updateAccounts}
          updateIndex={updateIndex}
          pathCode={pathCode}
        />
        <List.Accordion
          title={`Account ${currentIndex + 1}`}
          expanded={expanded}
          onPress={handlePress}>
          {renderAccountItems()}
        </List.Accordion>

        <View style={styles.addAccountButton}>
          <Button
            mode="contained"
            onPress={addAccountHandler}
            loading={isAccountCreating}>
            {isAccountCreating ? 'Adding' : 'Add Account'}
          </Button>
        </View>

        <View style={styles.addAccountButton}>
          <Button
            mode="contained"
            onPress={() => {
              setHdDialog(true);
              // TODO: Use coin type while adding from HD path
              setPathCode(
                selectedNetwork!.namespace === EIP155
                  ? "m/44'/60'/"
                  : "m/44'/118'/",
              );
            }}>
            Add Account from HD path
          </Button>
        </View>

        <AccountDetails account={accounts[currentIndex]} />

        <View style={styles.signLink}>
          <TouchableOpacity
            onPress={() => {
              navigation.navigate('SignMessage', {
                selectedNamespace: selectedNetwork!.namespace,
                selectedChainId: selectedNetwork!.chainId,
                accountInfo: accounts[currentIndex],
              });
            }}>
            <Text
              variant="titleSmall"
              style={[styles.hyperlink, { color: theme.colors.primary }]}>
              Sign Message
            </Text>
          </TouchableOpacity>
        </View>

        <View style={styles.signLink}>
          <TouchableOpacity
            onPress={() => {
              navigation.navigate('AddNetwork');
            }}>
            <Text
              variant="titleSmall"
              style={[styles.hyperlink, { color: theme.colors.primary }]}>
              Add Network
            </Text>
          </TouchableOpacity>
        </View>
      </View>
    </ScrollView>
  );
};

export default Accounts;
