/*
 * last modified---
 * 	03-06-25 new
 *
 * purpose---
 * 	display UI to show and save all wallet eNFTs
 */

import React, { useState } from 'react';
import Container from 'react-bootstrap/Container';
import useEth from './EthContext/useEth';
import MVOComm from './MVOComm.js';
import LoadingButton from './LoadingButton.jsx';
import DataToast from './DataToast.jsx';
import NoticeWrongNetwork, { NoticeNoArtifact } from './Notices.jsx';


/* render the page to support display and copy/paste of eNFT JSON + keys
 * @param props.parseReplyMethod method to process replies from MVOs
 * @param props.eNFTList the list of downloaded eNFTs
 */
function DownloadWallet(props) {
	const { state: { accounts, contracts, chainConn, artifacts, web3 } }
		= useEth();

	// state storage of passed eNFTs, downloaded and decrypted and passed down
	const [eNFTs, setENFTs] = useState([]);

	// local state for eNFT DataToast contents
	const [toastData, setToastData] = useState("");

	// audit that passed eNFTs (if any) are known (NB: all are decrypted)
	const newEnfts = [];
	props.eNFTList.forEach(enft => {
		const existEnft = eNFTs.find(elt => elt.id === enft.id);
		if (existEnft === undefined) {
			// add
			newEnfts.push(enft);
		}
	});
	if (newEnfts.length > 0) {
		if (eNFTs.length === 0) {
			setENFTs([...newEnfts]);
		} else {
			setENFTs([...eNFTs, ...newEnfts]);
		}
	}

	// if we have any, parse eNFTs into Json for toast display
	var enftJsonData = '';
	if (eNFTs.length > 0) {
		// count the number of decrypted and sig-verified eNFTs
		let enftCnt = 0;
		eNFTs.forEach(enft => {
			if (enft.validateSig(chainConn, web3)) enftCnt++;
		});
		let enftIdx = 0;
		enftJsonData += "[";
		eNFTs.forEach(enft => {
			// only sig-valid eNFTs are shown
			if (enft.valid) {
				/* NB: we do not concern ourselves with whether or not the
				 * eNFT is currently valid/confirmed, or whether or not the
				 * eNFT has been greylisted by an Auditor.  This is because
				 * these conditions are temporary, and the purpose of this
				 * list is to create an accurate list of all minted+unburned
				 * eNFTs owned by the wallet, for saving in a backup file.
				 * We therefore override the valid property without triggering
				 * a call to Enft.isAvailable().
				 */
				enft.avail = true;
				let json = JSON.stringify(enft);
				if (++enftIdx < enftCnt) {
					json += ",\n";
				}
				enftJsonData += json;
			}
		});
		enftJsonData += "]";

		if (toastData !== enftJsonData) {
			setToastData(enftJsonData);
		}
	}

	/* method to send a signed wallet download request to an MVO
	 * NB: the MVO contacted will return a list of minted eNFTs, with all of
	 * the burned eNFTs pre-subtracted from it.  It does not guarantee that any
	 * given eNFT is currently available or has not been greylisted by an AUD.
	 */
	async function sendWalletReqToMVO(resolve, reject) {
		// examine passed MVO configuration to ensure it's been downloaded
		const mvoConfig = chainConn.MVOConf;
		const chId = chainConn.chainConfig.chainId;
		if (mvoConfig.availableMVOs.length === 0) {
			let inputErr = new Error("No MVOs listed for chainId " + chId
					+ "; is " + chainConn.chainConfig.chain + " connected?");
			alert(inputErr.message);
			reject(inputErr);
			return false;
		}

		// obtain secure communicator to randomly selected MVO for this chain
		const mvoComm = mvoConfig.getMVOCommunicator('wallet', true);
		if (!(mvoComm instanceof MVOComm)) {
			let mvoErr = new Error("Could not select an MVO");
			alert(mvoErr.message);
			reject(mvoErr);
			return false;
		}

		// access msg.sender and verifying contract address
		const sender = accounts[0];
		const enshContract = contracts["EnshroudProtocol"];
		const enshAddress = enshContract.options.address;

		// generate reply key and the payload we must sign
		var replyKey = '';
		var payload = '';
		if (!mvoComm.encrypted) {
			// old version, for use without encryption (passed as POST param)
			payload = 'walletDownload={"chainId":"' + chId
						+ '","sender":"' + sender + '","IDList":[]}';

			// send plain data unencrypted and unsigned
			mvoComm.sendToMVO(payload, props.parseReplyMethod);
		}
		else {
			// generate an AES key for the MVO to use to encrypt normal replies
			mvoComm.generateAesKey();
			// NB: generateAesKey() stored raw key in mvoComm.replyKeyB64
			replyKey = mvoComm.decryptKey;

			// define eth_signTypedData_v4 parameters
			const msgParams = JSON.stringify({
				// EIP-712 domain info (depends upon chId scan URL for display)
				domain: {
					chainId: chId,
					name: 'Enshroud',
					verifyingContract: enshAddress,
					version: '1',
				},
				// descriptive info on what's being signed and for whom
				message: {
					contents: 'Send encrypted request to download the eNFTs and keys in your wallet',
					to: {
						MVOId: mvoComm.mvo,
						URL: mvoComm.mvoURL,
					},
					requestJson: {
						walletDownload: {
							chainId: `${chId}`,
							sender: sender,
							IDList: [],
							replyKey: replyKey,
						},
					},
				},
				primaryType: 'Request',
				types: {
					// the domain the contract is hosted on
					EIP712Domain: [
						{ name: 'chainId', type: 'uint256' },
						{ name: 'name', type: 'string' },
						{ name: 'verifyingContract', type: 'address' },
						{ name: 'version', type: 'string' },
					],
					// refer to primaryType
					Request: [
						{ name: 'contents', type: 'string' },
						{ name: 'to', type: 'MVO' },
						{ name: 'requestJson', type: 'WalletDownload' },
					],
					// not an EIP712Domain definition
					MVO: [
						{ name: 'MVOId', type: 'string' },
						{ name: 'URL', type: 'string' },
					],
					// not an EIP712Domain definition
					WalletDownload: [
						{ name: 'walletDownload', type: 'Payload' },
					],
					// not an EIP712Domain definition
					Payload: [
						{ name: 'chainId', type: 'string' },
						{ name: 'sender', type: 'address' },
						{ name: 'IDList', type: 'string[]' },
						{ name: 'replyKey', type: 'string' },
					],
				},
			});
			const method = 'eth_signTypedData_v4';
			var params = [sender, msgParams];

			// now obtain signature on params in a EIP-712 compatible way
			var userSig;
			await web3.currentProvider.sendAsync(
				{
					method,
					params,
					from: sender,
				},
				function (err, result) {
					if (err) console.dir(err);
					if (result.error) alert(result.error.message);
					if (result.error) console.error('ERROR', result.error);
					userSig = result.result;
					if (userSig === undefined) {
						let sigErr
							= new Error("Error building EIP712 signature");
						reject(sigErr);
						return false;
					}

					// append signature to the arguments
					const allArgs = JSON.parse(msgParams);
					allArgs.signature = userSig;

					// encrypt + send the message to the MVO, passing callback
					mvoComm.sendToMVO(JSON.stringify(allArgs),
									  props.parseReplyMethod);
					// NB: even if sendToMVO() fails, we still want to resolve
					resolve(true);
				}
			);
		}

		// clear the existing eNFT list
		eNFTs.splice(0, eNFTs.length);
		return true;
	}

	// build the page
	const downloadPage =
	<div className="container">
		<Container fluid align="left">
			<h2>Wallet Download for Backup</h2>
			<br/>
			<p className="text-muted">
				<b>Note:</b> normally you will not need to use this function.
				Instead, simply use the "Refresh" button on the Spend or Burn
				screens to populate the table with a list of your existing
				eNFTs.  This backup method is provided in case of a situation in
				which the Enshroud key servers have suffered a loss of the keys
				required to decrypt your eNFTs from the blockchain.<br/>
				<i>("Not your keys, not your coins.")</i>
			</p>
			<br/>
			<h4>Procedure for Saving the eNFTs in your Wallet:</h4>
			<ol>
				<li>
					Use the "Download" button below to refresh the list of
					eNFTs in your account as of the current block.  This
					operation will require your signature.
				</li>
				<li>
					Use the "Display JSON" button below.  The decrypted
					eNFTs will be displayed as JSON data, together with
					their associated AES decryption keys (which decrypt the
					URI data published on the blockchain). <b>Note:</b> all
					eNFTs are assumed to be available, even if not yet
					confirmed or currently greylisted.
				</li>
				<li>
					Copy and paste the displayed JSON data into a text file,
					using your favorite text editor (e.g. Notepad, vi).
					Store this file securely, as a <i>.txt</i> or <i>.json</i>.
					(But note that your account signing key is still needed to
					conduct any operations using your eNFTs, even with a
					decrypted copy of any eNFT.)
				</li>
				<li>
					Alternatively, you can print the window, take a
					screen shot of the selected area, or otherwise save
					the JSON data.  A save file is best for restoring a
					backup later, however.  (See "Restore Wallet Backup" in
					the Full Navigation Menu.)
				</li>
			</ol>
			<p className="text-muted">
				<b>Note that for security reasons, it is not permissible
				for the Enshroud dApp to write to your local disk.</b>
			</p>
			<br/><br/>
		</Container>
		<Container fluid align="center">
			<h4>
				1. &nbsp;<LoadingButton
					variant="primary"
					buttonStyle="m-3"
					netMethod={(resolve, reject) => sendWalletReqToMVO(resolve, reject)}
					buttonTitle="This fetches and decrypts the eNFT list and AES keys from an MVO"
					buttonText="Download"
					buttonIcon="images/download.svg"
				/>
				<i>(signature required)</i>
			</h4>
			<br/><br/>
			<div aria-live="polite" aria-atomic="true"
				className="row mb-3 position-relative"
			>
				<h4>
					2. &nbsp;<DataToast variant="info"
						title="Decrypted eNFT data"
						buttonText="Display JSON"
						buttonIcon="images/key.svg"
						buttonTitle="Display decrypted eNFT data and keys (JSON format)"
						minWidth="768"
						data={toastData}
					/>
				</h4>
			</div>
			<br/><br/>
			<h4>
				3. &nbsp;Manually save the displayed JSON data above to a file
			</h4>
			<br/><br/>
		</Container>
	</div>

	// render the page
	return (
		<div id="Backup">
		{
			!artifacts.EnshroudProtocol ? <NoticeNoArtifact /> :
			contracts == null ||
					!contracts["EnshroudProtocol"] ? <NoticeWrongNetwork /> :
				downloadPage
		}
		</div>
	);
}

export default DownloadWallet;
