import React, { useEffect, useState } from "react";

import BigNumber from "bignumber.js";
import { RateService } from "rm-sdk";
import { Token } from "rm-sdk/build/interfaces";
import { Policy } from "rm-sdk/build/types";
import { CurrencyInput } from "../../../components/CurrencyInput";
import { Button, LabelText } from "../../../components/ui/Widget";
import { QuoteLoader } from "../../../helpers/quote-loader";
import { toSuffixFormat } from "../../../utils/configureFormats";

import "./styles.scss";
import { Web3Provider } from "@ethersproject/providers";
import { checkProviderState, processErrorMessage } from "../../../utils/commonUtils";

export const PolicyView = (props: {
  policy: Policy | undefined,
  quoteLoader: QuoteLoader,
  desiredCover: string,
  topUpValue: BigNumber | undefined,
  handleDesiredCoverChanged: () => void,
  handleTopUpValueChanged: () => void,
  rate: BigNumber | undefined,
  provider: Web3Provider,
  onPolicyCancelActivate?: (policy: Policy) => void,
}) => {
  const rateService = new RateService();

  const [coverIsEditing, setCoverIsEditing] = useState(false);
  const [cover, setCover] = useState<BigNumber | undefined>();
  const [deposit, setDeposit] = useState<BigNumber | undefined>();
  const [depositIsEditing, setDepositIsEditing] = useState(false);
  const [capitalTokenPriceUsd, setCapitalTokenPriceUsd] = useState(0);
  const [premiumTokenPriceUsd, setPremiumTokenPriceUsd] = useState(0);
  const [error, setError] = useState<string>();
  const [providerError, setProviderError] = useState<string>();
  const [premiumTokenAllowance, setPremiumTokenAllowance] = useState<BigNumber>();
  const [isLoading, setLoading] = useState(false);

  const initCover = new BigNumber(props.policy?.coverage ?? 0).div(`1e${props.quoteLoader.capitalToken?.decimals ?? 0}`);
  const initDeposit = new BigNumber(props.policy?.balance ?? 0).div(`1e${props.quoteLoader.premiumToken?.decimals ?? 0}`);

  useEffect(() => {
    (async () => {
      const r = await checkProviderState(props.provider, props.quoteLoader);

      setProviderError(r);
    })();
  }, [props.provider, props.quoteLoader]);

  useEffect(() => {
    if (props.policy && props.quoteLoader.capitalToken && props.quoteLoader.premiumToken) {
      setCover(initCover);
      setDeposit(initDeposit);
    }
  }, [props.policy, props.quoteLoader.capitalToken, props.quoteLoader.premiumToken]);

  const setTokenPrice = async (tokenInfo: Token | undefined, setter: React.Dispatch<React.SetStateAction<number>>) => {
    if (tokenInfo?.address) {
      setter(await rateService.getPrice(tokenInfo.symbol, tokenInfo.name));
    }
  };

  useEffect(() => {
    setTokenPrice(props.quoteLoader.capitalToken, setCapitalTokenPriceUsd);
  }, [props.quoteLoader.capitalToken?.address]);

  useEffect(() => {
    setTokenPrice(props.quoteLoader.premiumToken, setPremiumTokenPriceUsd);
    checkAllowance();
  }, [props.quoteLoader.premiumToken?.address]);

  const handleCoverChanged = (value: string) => {
    setCover(new BigNumber(value || 0));
  }

  const checkAllowance = async () => {
    const r = await props.quoteLoader.getPremiumTokenAllowance(
      props.provider
    );

    if (!r) {
      return;
    }

    setPremiumTokenAllowance(new BigNumber(r));
  }

  const handleDepositChanged = (value: string) => {
    setDeposit(new BigNumber(value || 0));
  }

  const editingCoverCancelled = (e: any) => {
    e.stopPropagation();

    setCoverIsEditing(false);
    setCover(initCover);
  };

  const handleUnlockToken = async () => {
    setLoading(true);
    setError(undefined);

    try {
      const r = await props.quoteLoader.approvePremiumToken(props.provider);

      if (!r) {
        setLoading(false);

        return;
      }

      await r.wait(1);

      await checkAllowance();
    } catch (e) {
      setError((e as any)?.message ? processErrorMessage((e as any).message) : "Error");

      console.error(e);
    }

    setLoading(false);
  }

  const handleCancelPolicy = async (policy: Policy) => {
    setLoading(true);
    setError(undefined);

    try {
      const r = await props.quoteLoader.cancelPolicy(props.provider, policy);

      if (!r) {
        setLoading(false);

        return;
      }

      const txr = await r.wait(1);

      await props.quoteLoader.waitUntilGraphIsSynced(txr.blockNumber);
      await props.onPolicyCancelActivate?.(policy);

      setLoading(false);
    } catch (e) {
      setError((e as any)?.message ? processErrorMessage((e as any).message) : "Error");

      console.error(e);

      setLoading(false);
    }
  }

  const editingCoverConfirmed = async (e: any, newCover: BigNumber | undefined, newDeposit?: BigNumber) => {
    setError(undefined);
    e?.stopPropagation();

    setCoverIsEditing(false);

    if (initCover.isZero() && !newDeposit) {
      return;
    }

    if (!newCover || !props.policy || !props.quoteLoader.capitalToken ||
      new BigNumber(newCover).times(`1e${props.quoteLoader.capitalToken.decimals}`).eq(props.policy.coverage)) {
      setCover(initCover);
      setError("New cover is equal to current one.");

      return;
    }

    try {
      setLoading(true);

      const r = await props.quoteLoader.sdk.changePolicyCoverageDeposit(
        props.policy.id,
        props.policy.productId,
        props.policy.market.riskPoolsControllerAddress,
        new BigNumber(newCover).times(`1e${props.quoteLoader.capitalToken.decimals}`).toString(),
        new BigNumber(newDeposit ?? "0").times(`1e${props.quoteLoader.premiumToken?.decimals}`).toString(),
        props.provider,
      );

      const txr = await r.wait(1);

      await props.quoteLoader.waitUntilGraphIsSynced(txr.blockNumber);
      await props.onPolicyCancelActivate?.(props.policy);

      setLoading(false);
    } catch (e) {
      setError((e as any)?.message ? processErrorMessage((e as any).message) : "Error");

      console.error(e);

      setCover(initCover);
      setLoading(false);
    }
  };

  const editingDepositCancelled = (e: any) => {
    e.stopPropagation();
    setDepositIsEditing(false);
    setDeposit(initDeposit);
  };

  const handleDepositKeyDown = (e: any) => {
    if (e.key === 'Enter') {
      editingDepositConfirmed(e);
    }

    if (e.key === 'Escape') {
      editingDepositCancelled(e);
    }
  }

  const handleCoverKeyDown = (e: any) => {
    if (e.key === 'Enter' && cover) {
      editingCoverConfirmed(e, cover);
    }

    if (e.key === 'Escape') {
      editingCoverCancelled(e);
    }
  }

  const editingDepositConfirmed = async (e: any) => {
    setError(undefined);

    e.stopPropagation();
    setDepositIsEditing(false);

    if (initCover.isZero()) {
      return;
    }

    if (!props.policy || !deposit || !props.quoteLoader.premiumToken || !props.quoteLoader.capitalToken ||
      new BigNumber(deposit).times(`1e${props.quoteLoader.premiumToken.decimals}`).minus(props.policy.balance).lte(0)) {
      setDeposit(initDeposit);
      setError("New deposit can not be less than current one.");

      return;
    }

    try {
      setLoading(true);

      const r = await props.quoteLoader.sdk.topUpPolicy(
        props.policy.id,
        props.policy.market.riskPoolsControllerAddress,
        new BigNumber(deposit).times(`1e${props.quoteLoader.premiumToken.decimals}`).minus(props.policy.balance).toString(),
        props.provider,
      );

      const txr = await r.wait(1);

      await props.quoteLoader.waitUntilGraphIsSynced(txr.blockNumber);
      await props.onPolicyCancelActivate?.(props.policy);

      setLoading(false);
    } catch (e) {
      setError((e as any)?.message ? processErrorMessage((e as any).message) : "Error");

      console.error(e);

      setDeposit(initDeposit);
      setLoading(false);
    }
  };

  const startDepositEditing = (e: any) => {
    e.stopPropagation();
    setCoverIsEditing(false);
    setDepositIsEditing(true);
    setError(undefined);
  };

  const startCoverEditing = (e: any) => {
    e.stopPropagation();
    setCoverIsEditing(true);
    setDepositIsEditing(false);
    setError(undefined);
  };

  return <>
    <LabelText valueColor={props.policy?.coverage === "0" && !initCover.eq(cover ?? 0) ? "gray" : undefined}>
      <div>Policy #</div>
      <div>{props.policy?.policyId}</div>
    </LabelText>

    {!coverIsEditing &&
      <LabelText valueColor={props.policy?.coverage === "0" && !initCover.eq(cover ?? 0) ? "gray" : undefined}>
        <div>Policy Desired Cover <a onClick={startCoverEditing}>(edit)</a></div>
        <div>{(cover && toSuffixFormat(cover)) || "-"} {props.quoteLoader.capitalToken?.symbol}</div>
      </LabelText>
    }
    {coverIsEditing &&
      <LabelText>
        <CurrencyInput
          currencyLogo={props.quoteLoader.capitalToken?.logoURI}
          subLabels={[
            props.quoteLoader.capitalToken?.symbol || "",
            (cover && capitalTokenPriceUsd && `($${new BigNumber(cover).times(capitalTokenPriceUsd).toFixed(2)})`) || "",
          ]}
          value={cover?.toFixed() || ""}
          onChange={handleCoverChanged}
          onKeyDown={handleCoverKeyDown}
          onBlur={editingCoverCancelled}
          label={<div>
            Desired cover amount
            <span className="c-policy-view__confirm-icon" onMouseDown={(e) => editingCoverConfirmed(e, cover)}>✔️</span>
            <span className="c-policy-view__cancel-icon" onClick={editingCoverCancelled}>✖️</span>
          </div>}
          placeholder="e.g. 10 000.00"
        />
      </LabelText>
    }
    {!depositIsEditing &&
      <LabelText valueColor={props.policy?.coverage === "0" && !initDeposit.eq(deposit ?? 0) ? "gray" : undefined}>
        <div>Policy Deposit <a onClick={startDepositEditing}>(edit)</a></div>
        <div>{(deposit && toSuffixFormat(deposit, 2, BigNumber.ROUND_UP)) || "-"} {props.quoteLoader.premiumToken?.symbol}</div>
      </LabelText>
    }
    {depositIsEditing &&
      <LabelText>
        <CurrencyInput
          currencyLogo={props.quoteLoader.premiumToken?.logoURI}
          subLabels={[
            props.quoteLoader.premiumToken?.symbol || "",
            (deposit && premiumTokenPriceUsd && `($${new BigNumber(deposit).times(premiumTokenPriceUsd).toFixed(2)})`) || "",
          ]}
          value={deposit?.toFixed() || ""}
          onChange={handleDepositChanged}
          onKeyDown={handleDepositKeyDown}
          onBlur={editingDepositCancelled}
          label={<div>
            Top-up
            <span className="c-policy-view__confirm-icon" onMouseDown={editingDepositConfirmed}>✔️</span>
            <span className="c-policy-view__cancel-icon" onClick={editingDepositCancelled}>✖️</span>
          </div>
          }
          placeholder="e.g. 10 000.00"
        />
      </LabelText>
    }

    <LabelText><div>APR:</div> <div>{props.rate ? `${props.rate?.toFixed(2, BigNumber.ROUND_UP)}%` : "-"}</div></LabelText>

    <div className='button-block'>
      <Button disabled={!!providerError || !premiumTokenAllowance || premiumTokenAllowance.gte(deposit ?? 0) || isLoading} onClick={handleUnlockToken} className="unlock-button">Unlock {props.quoteLoader.premiumToken?.symbol}</Button>
      {
        ((!props.policy || +props.policy?.coverage) &&
        <Button disabled={!!providerError || isLoading || coverIsEditing || depositIsEditing} onClick={() => handleCancelPolicy(props.policy!)}>Cancel Protection</Button>) || ""
      }
      {
        (props.policy && !+props.policy.coverage &&
        <Button disabled={!!providerError || isLoading || cover?.isZero() || deposit?.isZero() || premiumTokenAllowance?.lt(deposit ?? 0) || coverIsEditing || depositIsEditing} onClick={(e) => editingCoverConfirmed(e, cover, deposit)}>Activate Protection</Button>) || ""
      }
    </div>

    {!props.policy?.id &&
      <LabelText className="warning"> Pending activation... </LabelText>
    }

    {(error || providerError) &&
      <LabelText className="error">{error || providerError}</LabelText>
    }

    {!(error || providerError) && props.policy?.id &&
      <div>&nbsp;</div>
    }
  </>;
}
