import React, { useEffect, useMemo, useState } from 'react';
import { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { MsgCreateValidator } from 'cosmjs-types/cosmos/staking/v1beta1/tx';

import { Box, Link, MenuItem, Select, TextField, Typography } from '@mui/material';
import { fromBech32, toBech32 } from '@cosmjs/encoding';
import { LoadingButton } from '@mui/lab';
import { EncodeObject, encodePubkey } from '@cosmjs/proto-signing';
import { Registry } from '@cerc-io/registry-sdk';

import { useWalletConnectContext } from '../context/WalletConnectContext';
import { Participant } from '../types';

const Validator = () => {
  const { session, signClient, isSessionLoading } = useWalletConnectContext();
  const navigate = useNavigate();

  const [cosmosAddress, setCosmosAddress] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [moniker, setMoniker] = useState('');
  const [pubKey, setPubKey] = useState('');
  const [participant, setParticipant] = useState<Participant | null>(null);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    if (isSessionLoading) {
      return;
    }

    if (!session) {
      navigate("/connect-wallet?redirectTo=validator");
    }
  }, [session, navigate, isSessionLoading]);

  useEffect(() => {
    if (!cosmosAddress) {
      setParticipant(null);
      return;
    }

    const fetchParticipant = async () => {
      const registry = new Registry(process.env.REACT_APP_REGISTRY_GQL_ENDPOINT!);

      try {
        const fetchedParticipant = await registry.getParticipantByAddress(cosmosAddress);
        if (fetchedParticipant) {
          setParticipant(fetchedParticipant);
        } else {
          throw new Error("Participant not found");
        }
      } catch (error) {
        console.error("Error fetching participant", error);
        enqueueSnackbar("Error in fetching participant", { variant: "error" });
        setParticipant(null);
      }
    };

    fetchParticipant();
  }, [cosmosAddress]);

  const isMonikerValid = useMemo(() => moniker.trim().length > 0, [moniker]);
  const isPubKeyValid = useMemo(() => pubKey.length === 44, [pubKey]);

  const msgCreateValidator: MsgCreateValidator = useMemo(() => {
    const encodedPubKey = encodePubkey({
      type: "tendermint/PubKeyEd25519",
      value: pubKey.length === 44 ? pubKey : '',
    });

    return {
      description: {
        moniker,
        identity: "",
        website: "",
        securityContact: "",
        details: "",
      },
      commission: {
        maxChangeRate: "10000000000000000", // 0.01
        maxRate: "200000000000000000", // 0.2
        rate: "100000000000000000", // 0.1
      },
      minSelfDelegation: "1",
      delegatorAddress: '',
      validatorAddress: cosmosAddress && toBech32('laconicvaloper', fromBech32(cosmosAddress).data),
      pubkey: encodedPubKey,
      value: {
        amount: process.env.REACT_APP_STAKING_AMOUNT!,
        denom: process.env.REACT_APP_LACONICD_DENOM!,
      },
    };
  }, [cosmosAddress, pubKey, moniker]);

  const msgCreateValidatorEncodeObject: EncodeObject = {
    typeUrl: '/cosmos.staking.v1beta1.MsgCreateValidator',
    value: MsgCreateValidator.toJSON(msgCreateValidator),
  };

  const sendTransaction = async () => {
    if (
      !isMonikerValid ||
      !isPubKeyValid ||
      !msgCreateValidator.validatorAddress
    ) {
      setIsError(true);
      return;
    }

    setIsLoading(true);
    enqueueSnackbar("View and sign the message from your Laconic Wallet", { variant: "info" });

    try {
      const params = { transactionMessage: msgCreateValidatorEncodeObject, signer: cosmosAddress };
      const response = await signClient!.request<{ code: number }>({
        topic: session!.topic,
        chainId: `cosmos:${process.env.REACT_APP_LACONICD_CHAIN_ID}`,
        request: {
          method: "cosmos_sendTransaction",
          params,
        },
      });

      if (response.code !== 0) {
        throw new Error("Transaction not sent");
      } else {
        navigate("/validator-success", { state: { validatorAddress: msgCreateValidator.validatorAddress, } });
      }
    } catch (error) {
      console.error("Error sending transaction", error);
      enqueueSnackbar("Error in sending transaction", { variant: "error" });
    } finally {
      setIsLoading(false);
    }
  };

  const replacer = (key: string, value: any): any => {
    if (value instanceof Uint8Array) {
      return Buffer.from(value).toString('hex');
    }
    return value;
  };

  return (
    <Box sx={{ display: "flex", flexDirection: "column", marginTop: 6, gap: 1 }}>
      <Typography variant="h5">Create a validator</Typography>
      <Typography variant="body1">Select Laconic account:</Typography>
      <Select
        sx={{ marginBottom: 2 }}
        id="cosmos-address-select"
        value={cosmosAddress}
        onChange={(e) => setCosmosAddress(e.target.value)}
        style={{ maxWidth: "600px", display: "block" }}
      >
        {session?.namespaces.cosmos.accounts.map((address, index) => (
          <MenuItem value={address.split(":")[2]} key={index}>
            {address.split(":")[2]}
          </MenuItem>
        ))}
      </Select>

      {Boolean(cosmosAddress) && (
        <>
          {participant ? (
            <Typography>Onboarded participant</Typography>
          ) : (
            <Typography>No participant found</Typography>
          )}

          <Box
            sx={{
              backgroundColor: participant ? "lightgray" : "white",
              padding: 3,
              wordWrap: "break-word",
              marginBottom: 3,
            }}
          >
            {participant && (
              <pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
                Cosmos Address: {participant.cosmosAddress} <br />
                Nitro Address: {participant.nitroAddress} <br />
                Role: {participant.role} <br />
                KYC ID: {participant.kycId} <br />
              </pre>
            )}
          </Box>

          {participant?.role === "validator" && (
            <>
              <Box style={{ maxWidth: "600px" }}>
                <TextField
                  id="moniker"
                  label="Enter your node moniker (example: AliceNode)"
                  variant="outlined"
                  fullWidth
                  margin="normal"
                  value={moniker}
                  onChange={(e) => {
                    setIsError(false);
                    setMoniker(e.target.value);
                  }}
                  error={!isMonikerValid && isError}
                  helperText={!isMonikerValid && isError ? "Moniker is required" : ""}
                />
              </Box>

              <Typography sx={{ marginTop: 3}}>
                Fetch your validator public key using the following command (refer&nbsp;
                <Link
                  href="https://git.vdb.to/cerc-io/testnet-laconicd-stack/src/branch/main/testnet-onboarding-validator.md#join-as-testnet-validator"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  this guide
                </Link>
                    )
              </Typography>

              <Box sx={{ backgroundColor: "lightgray", padding: 3, wordWrap: "break-word" }}>
                <pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
                  {`laconic-so deployment --dir testnet-laconicd-deployment exec laconicd "laconicd cometbft show-validator" | jq -r .key`}
                </pre>
              </Box>

              <Box sx={{ maxWidth: "600px" }}>
                <TextField
                  id="pub-key"
                  label="Enter your validator public key"
                  variant="outlined"
                  fullWidth
                  margin="normal"
                  value={pubKey}
                  onChange={(e) => {
                    setIsError(false);
                    setPubKey(e.target.value);
                  }}
                  error={!isPubKeyValid && isError}
                  helperText={!isPubKeyValid && isError ? "Public key must be 44 characters" : ""}
                />
              </Box>
              <Typography>Send transaction to chain</Typography>
              <Box sx={{ backgroundColor: "lightgray", padding: 3, wordWrap: "break-word" }}>
                <pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
                  {JSON.stringify(msgCreateValidator, replacer, 2)}
                </pre>
              </Box>
              <Box marginTop={1} marginBottom={1}>
                <LoadingButton
                  variant="contained"
                  onClick={sendTransaction}
                  loading={isLoading}
                  disabled={isError}
                >
                  Send transaction
                </LoadingButton>
              </Box>
            </>
          )}
        </>
      )}
    </Box>
  );
};

export default Validator;
