import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useSnackbar } from 'notistack';
import BN from 'bn.js';
import {
  Account,
  PublicKey,
  SystemProgram,
  SYSVAR_RENT_PUBKEY,
  SYSVAR_CLOCK_PUBKEY,
} from '@solana/web3.js';
import { TokenInstructions } from '@project-serum/serum';
import { createTokenAccountInstrs } from '@project-serum/common';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
import InputLabel from '@material-ui/core/InputLabel';
import Select from '@material-ui/core/Select';
import FormHelperText from '@material-ui/core/FormHelperText';
import MenuItem from '@material-ui/core/MenuItem';
import Typography from '@material-ui/core/Typography';
import CircularProgress from '@material-ui/core/CircularProgress';
import DialogContent from '@material-ui/core/DialogContent';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogActions from '@material-ui/core/DialogActions';
import { State as StoreState } from '../../store/reducer';
import { ActionType } from '../../store/actions';
import { useWallet } from '../../components/common/WalletProvider';
import OwnedTokenAccountsSelect from '../../components/common/OwnedTokenAccountsSelect';
import { fromDisplay } from '../../utils/tokens';
import { vestingSigner } from '../../utils/lockup';
import { ViewTransactionOnExplorerButton } from '../common/Notification';

export default function NewVestingButton() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <div onClick={() => setOpen(true)}>
        <Button variant="contained" color="secondary">
          New
        </Button>
      </div>
      <NewVestingDialog open={open} onClose={() => setOpen(false)} />
    </>
  );
}

type NewVestingDialogProps = {
  open: boolean;
  onClose: () => void;
};

function NewVestingDialog(props: NewVestingDialogProps) {
  const { open, onClose } = props;
  const { network, accounts } = useSelector((state: StoreState) => {
    return {
      network: state.common.network,
      accounts: state.accounts,
    };
  });

  const defaultStartDate = new Date().toString();
  const defaultStartTs = new Date(defaultStartDate).getTime() / 1000;
  const defaultEndDate = '2027-01-01T12:00';
  const defaultEndTs = new Date(defaultEndDate).getTime() / 1000;

  const [beneficiary, setBeneficiary] = useState('');
  const isValidBeneficiary = (() => {
    try {
      new PublicKey(beneficiary);
      return true;
    } catch (_) {
      return false;
    }
  })();
  const displayBeneficiaryError = !isValidBeneficiary && beneficiary !== '';

  const [fromAccount, setFromAccount] = useState<null | PublicKey>(null);
  const [startTimestamp, setStartTimestamp] = useState(defaultStartTs);
  const [timestamp, setTimestamp] = useState(defaultEndTs);
  const [periodCount, setPeriodCount] = useState(7);
  const [displayAmount, setDisplayAmount] = useState<null | number>(null);
  const { lockupClient } = useWallet();
  const [isLoading, setIsLoading] = useState(false);
  const [mint, setMint] = useState<null | PublicKey>(null);

  const { enqueueSnackbar } = useSnackbar();
  const dispatch = useDispatch();

  const submitBtnEnabled =
    mint !== null &&
    fromAccount !== null &&
    isValidBeneficiary &&
    displayAmount !== null;

  const createVestingClickHandler = async () => {
    setIsLoading(true);
    try {
      const beneficiaryPublicKey = new PublicKey(beneficiary);
      const beneficiaryAccount = await lockupClient.provider.connection.getAccountInfo(
        beneficiaryPublicKey,
      );
      if (beneficiaryAccount === null) {
        enqueueSnackbar('Unable to validate given beneficiary.', {
          variant: 'error',
        });
        setIsLoading(false);
        return;
      }
      if (!beneficiaryAccount.owner.equals(SystemProgram.programId)) {
        enqueueSnackbar(
          'The beneficiary must be owned by the System Program.',
          {
            variant: 'error',
          },
        );
        setIsLoading(false);
        return;
      }

      enqueueSnackbar('Creating vesting acount...', {
        variant: 'info',
      });

      const mintAccount = accounts[mint!.toString()];
      let amount = mintAccount
        ? fromDisplay(displayAmount!, mintAccount.decimals)
        : new BN(displayAmount!);

      const vesting = new Account();
      const vestingVault = new Account();
      const _vestingSigner = await vestingSigner(
        lockupClient.programId,
        vesting.publicKey,
      );

      let tx = await lockupClient.rpc.createVesting(
        beneficiaryPublicKey,
        amount,
        _vestingSigner.nonce,
        new BN(startTimestamp),
        new BN(timestamp),
        new BN(periodCount),
        null,
        {
          accounts: {
            vesting: vesting.publicKey,
            vault: vestingVault.publicKey,
            depositor: fromAccount,
            depositorAuthority: lockupClient.provider.wallet.publicKey,
            tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
            rent: SYSVAR_RENT_PUBKEY,
            clock: SYSVAR_CLOCK_PUBKEY,
          },
          signers: [vesting, vestingVault],
          instructions: [
            await lockupClient.account.vesting.createInstruction(vesting),
            ...(await createTokenAccountInstrs(
              lockupClient.provider,
              vestingVault.publicKey,
              mint!,
              _vestingSigner.publicKey,
            )),
          ],
        },
      );
      // Only add to the local store if the lockup belongs to the current user.
      if (beneficiaryPublicKey.equals(lockupClient.provider.wallet.publicKey)) {
        const vestingAccount = await lockupClient.account.vesting(
          vesting.publicKey,
        );
        dispatch({
          type: ActionType.LockupCreateVesting,
          item: {
            vesting: {
              publicKey: vesting.publicKey,
              account: vestingAccount,
            },
          },
        });
      }
      enqueueSnackbar(`Vesting account created`, {
        variant: 'success',
        action: <ViewTransactionOnExplorerButton signature={tx} />,
      });
      onClose();
    } catch (err) {
      enqueueSnackbar(`Error creating vesting account: ${err.toString()}`, {
        variant: 'error',
      });
    }
    setIsLoading(false);
  };

  return (
    <Dialog open={open} onClose={onClose} fullWidth maxWidth="md">
      <DialogTitle>
        <Typography variant="h4" component="h2">
          New Vesting Account
        </Typography>
      </DialogTitle>
      <DialogContent>
        <div>
          {isLoading && (
            <div
              style={{
                width: '40px',
                marginLeft: 'auto',
                marginRight: 'auto',
                marginBottom: '24px',
              }}
            >
              <CircularProgress
                style={{ marginLeft: 'auto', marginRight: 'auto' }}
              />
            </div>
          )}
          <div style={{ display: 'flex', width: '100%' }}>
            <div>
              <FormControl variant="outlined" style={{ width: '200px' }}>
                <InputLabel>Mint</InputLabel>
                <Select
                  value={mint ? mint!.toString() : ''}
                  onChange={e =>
                    setMint(new PublicKey(e.target.value as string))
                  }
                >
                  {Object.keys(network.mints).map(m => (
                    <MenuItem value={network.mints[m].toString()}>
                      {m.toUpperCase()}
                    </MenuItem>
                  ))}
                  {/*<MenuItem value="custom">Custom</MenuItem>*/}
                </Select>
              </FormControl>
            </div>
            {false && (
              <div style={{ flex: 1, marginLeft: '10px' }}>
                <TextField
                  fullWidth
                  label="Custom mint"
                  value={mint ? mint!.toString() : ''}
                  onChange={e => setMint(new PublicKey(e.target.value))}
                />
                <FormHelperText>Mint of the token to lockup</FormHelperText>
              </div>
            )}
          </div>
          <div>
            <div style={{ display: 'flex', width: '100%' }}>
              <div style={{ flex: 1 }}>
                <FormControl fullWidth>
                  <InputLabel>From</InputLabel>
                  <OwnedTokenAccountsSelect
                    mint={mint}
                    onChange={(f: PublicKey) => setFromAccount(f)}
                  />
                  <FormHelperText>Token account to send from</FormHelperText>
                </FormControl>
              </div>
            </div>
          </div>
          <div style={{ marginTop: '24px' }}>
            <TextField
              fullWidth
              error={displayBeneficiaryError}
              helperText={displayBeneficiaryError && 'Invalid beneficiary'}
              label="Beneficiary"
              value={beneficiary}
              onChange={e => setBeneficiary(e.target.value)}
            />
            <FormHelperText>Owner of the new vesting account</FormHelperText>
          </div>
          <div
            style={{
              marginTop: '24px',
            }}
          >
            {false && (
              <FormHelperText style={{ color: 'blue' }}>
                Note: Amounts for custom mints (i.e., not SRM/MSRM) are in their
                raw, non-decimal form. Make sure to convert before entering into
                the fields here. For example, if a token has 6 decimals, then
                multiply your desired amount by 10^6.
              </FormHelperText>
            )}
            <TextField
              fullWidth
              label="Amount"
              type="number"
              value={displayAmount}
              InputProps={{ inputProps: { min: 0 } }}
              onChange={e => setDisplayAmount(parseFloat(e.target.value))}
            />
            <FormHelperText>
              Amount to deposit into the vesting account
            </FormHelperText>
          </div>
          <div
            style={{
              marginTop: '24px',
              display: 'flex',
            }}
          >
            <div style={{ flex: 1, marginRight: '10px' }}>
              <TextField
                fullWidth
                label="Start date"
                type="datetime-local"
                defaultValue={defaultStartDate}
                InputLabelProps={{
                  shrink: true,
                }}
                onChange={e => {
                  const d = new Date(e.target.value);
                  setStartTimestamp(d.getTime() / 1000);
                }}
              />
              <FormHelperText>Date when vesting begins</FormHelperText>
            </div>
            <div>
              <TextField
                disabled
                fullWidth
                label="Unix Timestamp"
                value={startTimestamp}
              />
            </div>
          </div>
          <div
            style={{
              marginTop: '24px',
              display: 'flex',
            }}
          >
            <div style={{ flex: 1, marginRight: '10px' }}>
              <TextField
                fullWidth
                label="End date"
                type="datetime-local"
                defaultValue={defaultEndDate}
                InputLabelProps={{
                  shrink: true,
                }}
                onChange={e => {
                  const d = new Date(e.target.value);
                  setTimestamp(d.getTime() / 1000);
                }}
              />
              <FormHelperText>Date when all tokens are vested</FormHelperText>
            </div>
            <div>
              <TextField
                disabled
                fullWidth
                label="Unix Timestamp"
                value={timestamp}
              />
            </div>
          </div>
          <div
            style={{
              marginTop: '24px',
            }}
          >
            <FormControl fullWidth>
              <TextField
                id="outlined-number"
                label="Period Count"
                type="number"
                InputLabelProps={{
                  shrink: true,
                }}
                variant="outlined"
                value={periodCount}
                onChange={e =>
                  setPeriodCount(parseInt(e.target.value) as number)
                }
                InputProps={{ inputProps: { min: 1 } }}
              />
              <FormHelperText>Number of vesting periods</FormHelperText>
            </FormControl>
          </div>
        </div>
      </DialogContent>
      <DialogActions>
        <Button onClick={onClose}>Cancel</Button>
        <Button
          variant="contained"
          type="submit"
          color="primary"
          disabled={!submitBtnEnabled || isLoading}
          onClick={() => createVestingClickHandler()}
        >
          Create
        </Button>
      </DialogActions>
    </Dialog>
  );
}
