import React, { useState, Fragment, useEffect } from 'react';
import {
  Box, Footer,
  StyledCheckbox, Title,
  WidgetWrapper, Button, LabelText, Spinner,
} from '../../components/ui/Widget';
import  { AtomicaLightLogo } from '../../components/ui/AtomicaLightLogo';
import "../../utils/configureFormats";
import Modal from '../../components/Modal';
import { Environment, Policy } from 'rm-sdk/build/types';
import BigNumber from "bignumber.js";
import { Web3Provider } from '@ethersproject/providers';
import WidgetTitle from '../../components/ui/WidgetTitle';
import { ActivateProtectionView } from './ActivateProtectionView';
import { QuoteLoader } from '../../helpers/quote-loader';
import { checkProviderState, getBuyPolicyUrl, getNetworkParams, getPolicyDetailUrl, processErrorMessage } from '../../utils/commonUtils';
import { toSuffixFormat } from '../../utils/configureFormats';
import { ArrowDownIcon } from '../../components/ArrowDownIcon';
import { PolicyView } from './PolicyView';
import { Tabs } from '../../components/Tabs';

import "./styles.scss";
import { ERRORS } from '../../helpers/constants';

const DEFAULT_ACTIVATE_PROTECTION_TITLE = "Protect my deposit at {rate}% per year at '{marketEntity}'";
const DEFAULT_VIEW_PROTECTION_TITLE = "Protection for {coverage} {coverageCurrency} is activated";

export const RMWidgetConstants = {
  DEFAULT_ACTIVATE_PROTECTION_TITLE,
  DEFAULT_VIEW_PROTECTION_TITLE,
};

export type ControlType = 'toggle' | 'checkbox' | 'button' | 'link' | 'website-link'

interface IProps {
  provider: Web3Provider;
  protectionAmount: string;
  marketId: string;
  environment: Environment | string;
  className?: string;
  controlType?: ControlType;
  activateProtectionTitle?: string;
  viewProtectionTitle?: string;
  frontendOperator?: string;
  referrer?: string;
  defaultDuration?: number;
  incomeApyRate?: string;
  inlineMode?: boolean;
}

enum WidgetStatus {
  Error = -1,
  Initializing = 0,
  Initialized,
  NoProtection,
  Protected,
}

enum TabNames {
  BuyNewProtection,
  MyPolicies,
}

const tabs = [
  {
    label: "Buy protection",
    id: TabNames.BuyNewProtection,
  },
  {
    label: "My policies",
    id: TabNames.MyPolicies,
  },
];

export function RMWidget({
  provider,
  protectionAmount,
  marketId,
  environment,
  className,
  controlType,
  activateProtectionTitle,
  viewProtectionTitle,
  frontendOperator,
  referrer,
  defaultDuration,
  incomeApyRate,
  inlineMode,
}: IProps) {
  const [quoteLoader, setQuoteLoader] = useState(new QuoteLoader(environment));

  const [status, setStatus] = useState(WidgetStatus.Initializing);
  const [desiredCover, setDesiredCover] = useState(protectionAmount || "0");
  const [policyList, setPolicyList] = useState<Policy[]>();
  const [modalIsOpen, setModalOpen] = useState<boolean>(false);
  const [total, setTotal] = useState<BigNumber>();
  const [topUpValue, setTopUpValue] = useState<BigNumber>();
  const [duration, setDuration] = useState(Number(defaultDuration) || 180);
  const [isLoading, setLoading] = useState(true);
  const [selectedTab, setSelectedTab] = useState<TabNames>(TabNames.BuyNewProtection);
  const [rate, setRate] = useState(quoteLoader.rate);
  const [isPremiumTokenApproved, setPremiumTokenApproved] = useState<boolean | null>(null);
  const [errorDuringActivation, setErrorDuringActivation] = useState<string | null>(null);
  const [error, setError] = useState<string>();
  const [isPolicyActive, setPolicyActive] = useState<boolean>();
  const [chainId, setChainId] = useState<number>();

  const desiredToken = quoteLoader.insuredToken || quoteLoader.capitalToken;

  useEffect(() => {
    providerChanged();
  }, [provider, quoteLoader.sdk.envConfig?.chainId]);

  useEffect(() => {
    if (defaultDuration) {
      handleDurationChanged(+defaultDuration);
    }
  }, [defaultDuration]);

  useEffect(() => {
    setStatus(WidgetStatus.Initializing);
    setError(undefined);
    setErrorDuringActivation(null);
    setPremiumTokenApproved(null);

    const qLoader = new QuoteLoader(environment);

    setQuoteLoader(qLoader);

    init(qLoader);
  }, [provider, environment, marketId, chainId]);

  useEffect(() => {
    if (status !== WidgetStatus.Initialized || isPremiumTokenApproved !== null) {
      return;
    }

    checkAllowanceAndBalance();
  }, [provider, total, marketId, status]);

  useEffect(() => {
    if (status === WidgetStatus.Initializing
      || status === WidgetStatus.Error
      || status === WidgetStatus.Protected
    ) {
      return;
    }

    calcQuote(quoteLoader);
  }, [desiredCover, status]);

  async function init(quoteLoader: QuoteLoader) {
    if (chainId && quoteLoader.sdk.envConfig?.chainId !== chainId) {
      setError(ERRORS.WRONG_NETWORK);
    }

    try {
      setLoading(true);

      const r = await quoteLoader.load(
        provider,
        duration,
        marketId,
        desiredCover,
        frontendOperator,
      );

      setLoading(false);

      if (!r) {
        setError('Market not found');
        setStatus(WidgetStatus.Error);

        return;
      }

      updateValues(quoteLoader);
      checkMarketStatus(quoteLoader);
      checkUserHasPolicies(quoteLoader);
    } catch (e) {
      console.error(e);

      setStatus(WidgetStatus.Error);
      setError((e as any)?.message ? (e as any).message : "Error");
    }
  }

  async function providerChanged() {
    const r = await checkProviderState(provider, quoteLoader);
    const params = await getNetworkParams(provider);

    setChainId(params?.chainId);
    setError(r);
    setStatus(WidgetStatus.Initialized);
  }

  async function calcQuote(quoteLoader: QuoteLoader) {
    try {
      setLoading(true);

      const r = await quoteLoader.calcQuote(
        provider,
        desiredCover,
        duration,
      );

      setLoading(false);

      if (!r) {
        return;
      }

      updateValues(quoteLoader);
    } catch (e) {
      setLoading(false);
    }
  }

  function updateValues(quoteLoader: QuoteLoader) {
    setTopUpValue(quoteLoader.topUpValue);
    setTotal(quoteLoader.quote);
    setRate(quoteLoader.rate);

    if (quoteLoader.monthlyQuote?.isZero() && duration !== 0) {
      setDuration(0);
    }
  }

  function scrollToPolicyList() {
    setSelectedTab(TabNames.MyPolicies);
  }

  async function handleBuyPolicy() {
    setLoading(true);
    setErrorDuringActivation(null);

    try {
      const deposit = quoteLoader.quote!.plus(quoteLoader.bonus || 0).toFixed() || '0';
      const tx = await quoteLoader.buyPolicy(
        provider,
        desiredCover,
        deposit,
        frontendOperator,
        referrer,
      );

      if (tx) {
        const txr = await tx.wait(1);
        const newPolicyEvent = txr.events?.find((item: any) => item.event === "LogNewPolicy");

        if (newPolicyEvent) {
          const policyId = newPolicyEvent.args?.[1].toString();

          setPolicyActive(true);
          setStatus(WidgetStatus.Protected);
          setPolicyList([{
            policyId,
            coverage: new BigNumber(desiredCover).times(`1e${desiredToken?.decimals ?? 0}`).toFixed(0),
            balance: deposit,
          } as Policy,
            ...(quoteLoader.policyList ?? [])]);

          scrollToPolicyList();
        }

        if (await quoteLoader.waitUntilGraphIsSynced(txr.blockNumber)) {
          setStatus(WidgetStatus.Protected);
          setPolicyActive(true);
        }

        checkUserHasPolicies(quoteLoader);
      }
    } catch (e) {
      console.error(e);

      setErrorDuringActivation((e as any)?.message ? processErrorMessage((e as any).message) : "Error");
    }

    setLoading(false);
  }

  async function checkAllowanceAndBalance() {
    const r = await quoteLoader.getPremiumTokenAllowance(
      provider
    );

    if (!r) {
      return;
    }

    setPremiumTokenApproved(new BigNumber(r).gte(quoteLoader.quote?.toFixed() || 0));

    const b = await quoteLoader.getPremiumTokenBalance(
      provider
    );

    if (!b) {
      return;
    }

    if (new BigNumber(b).lt(quoteLoader.quote?.toFixed() || 0)) {
      setErrorDuringActivation("You don't have enough premium token to activate protection.");
    } else {
      setErrorDuringActivation(null);
    }
  }

  async function handleUnlockToken() {
    setLoading(true);
    setErrorDuringActivation(null);

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

      if (!r) {
        setLoading(false);

        return;
      }

      await r.wait(1);

      await checkAllowanceAndBalance();
    } catch (e) {
      console.error(e);

      setErrorDuringActivation((e as any)?.message ? processErrorMessage((e as any).message) : "Error");
    }

    setLoading(false);
  }

  async function checkUserHasPolicies(quoteLoader: QuoteLoader) {
    const r = await quoteLoader.isUserHasPolicies(provider);

    setPolicyActive(r);

    setStatus(r ? WidgetStatus.Protected : WidgetStatus.NoProtection);
    setPolicyList(quoteLoader.policyList);

    setLoading(false);
  }

  async function checkMarketStatus(quoteLoader: QuoteLoader) {
    const r = await quoteLoader.isMarketOpened();

    if (!r) {
      setErrorDuringActivation("Market is locked, try again later.");
    }
  }

  function showBuyPolicyPopUp() {
    setModalOpen(true);
    setErrorDuringActivation(null);

    checkAllowanceAndBalance();

    if (isPolicyActive) {
      scrollToPolicyList();
    }
  }

  function closePopUp(e: any) {
    if (e.target === e.currentTarget) {
      setModalOpen(false);
    }
  }


  function handleDesiredCoverChanged(value: string): boolean {
    if (Number.isFinite(+value) && +value > 0) {
      setDesiredCover(value);

      return true;
    }

    return false;
  }

  async function handlePolicyCancelActivate(policy: Policy) {
    await checkUserHasPolicies(quoteLoader);
    await calcQuote(quoteLoader);
  }

  function handleDurationChanged(value: number): boolean {
    setDuration(value);

    if (quoteLoader.minDeposit && quoteLoader.monthlyQuote) {
      const topUp = quoteLoader.monthlyQuote.isZero() ? new BigNumber(0) : quoteLoader.monthlyQuote.times(value).div(30);

      quoteLoader.updateTopUpValue(topUp.toString());

      setTopUpValue(topUp);
      setTotal(quoteLoader.minDeposit.plus(topUp));

      return true;
    }

    return false;
  }

  function handleTopUpValueChanged(value: string): boolean {
    if (Number.isFinite(+value) && new BigNumber(value).gte(0)) {
      const t = new BigNumber(value)
        .toFixed(quoteLoader.premiumToken?.decimals || 2, BigNumber.ROUND_UP);

      quoteLoader.updateTopUpValue(t);

      setTopUpValue(quoteLoader.topUpValue);
      setTotal(quoteLoader.quote);

      if (quoteLoader.monthlyQuote) {
        setDuration(quoteLoader.duration ?? 0);
      }

      return true;
    }

    return false;
  }

  const toggleDetails = () => !modalIsOpen ? showBuyPolicyPopUp() : setModalOpen(false);

  const BuyNewProtection = <WidgetContent
    isPolicyActive={false}
    quoteLoader={quoteLoader}
    topUpValue={topUpValue}
    desiredCover={desiredCover}
    rate={rate}
    duration={duration}
    handleDesiredCoverChanged={handleDesiredCoverChanged}
    handleTopUpValueChanged={handleTopUpValueChanged}
    handleDurationChanged={handleDurationChanged}
    policy={undefined}
    error={error}
    isPremiumTokenApproved={isPremiumTokenApproved}
    isLoading={isLoading}
    handleUnlockToken={handleUnlockToken}
    handleBuyPolicy={handleBuyPolicy}
    errorDuringActivation={errorDuringActivation}
    provider={provider}
  />;

  const PolicyContent = (policy: Policy) => <WidgetContent
    isPolicyActive={isPolicyActive}
    quoteLoader={quoteLoader}
    topUpValue={topUpValue}
    desiredCover={desiredCover}
    rate={rate}
    duration={duration}
    handleDesiredCoverChanged={handleDesiredCoverChanged}
    handleTopUpValueChanged={handleTopUpValueChanged}
    handleDurationChanged={handleDurationChanged}
    policy={policy}
    isPremiumTokenApproved={isPremiumTokenApproved}
    isLoading={isLoading}
    handleUnlockToken={handleUnlockToken}
    handleBuyPolicy={handleBuyPolicy}
    provider={provider}
    onPolicyCancelActivate={handlePolicyCancelActivate}
  />;

  const Content = <>
    <Title className="title">
      <img className="light-logo" src="https://sdk.atomica.org/full_logo.svg" alt='Atomica logo'></img>
      <span className="dark-logo"><AtomicaLightLogo /></span>
      <p>Protection with Atomica</p>
    </Title>
    <Tabs tabs={tabs} selectedTabId={selectedTab} selectTab={setSelectedTab} />

    <div className='c-protection-widget__widget-content-container'>

      {selectedTab === TabNames.BuyNewProtection &&
        <div className='c-protection-widget__buy-protection-wrapper'>
          {BuyNewProtection}
        </div>
      }
      {selectedTab === TabNames.MyPolicies &&
        <>
          {(policyList?.length ?? 0) === 0 &&
            <div className='c-protection-widget__policy-title'>No policies yet.</div>
          }

          <div className='c-protection-widget__policy-card-container'>
            {
              policyList?.map((policy, i) => <div key={i} className='c-protection-widget__policy-card'>
                {PolicyContent(policy)}
              </div>)
            }
          </div>
        </>
      }
    </div>
  </>;

  function renderWidget(status: WidgetStatus) {
    switch (status) {
      case WidgetStatus.Error:
        return <div>{error}</div>;
      case WidgetStatus.Initializing:
      case WidgetStatus.Initialized:
        return <Spinner />;
      case WidgetStatus.NoProtection:
      case WidgetStatus.Protected:
        return (
        <div className={`c-protection-widget ${className === "dark-theme" ? className : "light-theme"}`}>
          <WidgetTitle
            activateProtectionTitle={activateProtectionTitle || DEFAULT_ACTIVATE_PROTECTION_TITLE}
            isPolicyActive={isPolicyActive!}
            activePolicyTitle={viewProtectionTitle || DEFAULT_VIEW_PROTECTION_TITLE}
            rate={rate?.toFixed(2, BigNumber.ROUND_UP)}
            coverageTokenInfo={desiredToken}
            coverage={isPolicyActive === false ?
              new BigNumber(desiredCover).times(`1e${desiredToken?.decimals ?? 0}`).toFixed(0) :
              policyList?.[0]?.underlyingCover !== "0" ? policyList?.[0]?.underlyingCover : policyList?.[0]?.coverage}
            quote={quoteLoader.quote?.toFixed(2, BigNumber.ROUND_UP)}
            quoteTokenInfo={quoteLoader.premiumToken}
            incomeApyRate={incomeApyRate}
            controlType={controlType}
            showBuyPolicyPopUp={showBuyPolicyPopUp}
            className={className}
            policyUrl={getPolicyDetailUrl(quoteLoader.uiUrl(), policyList?.[0]?.id)}
            marketUrl={getBuyPolicyUrl(quoteLoader.uiUrl(), marketId, frontendOperator, referrer)}
            market={quoteLoader.market}
            uiEntityList={quoteLoader.uiEntities}
          />
          {inlineMode &&
            <div className='c-protection-widget__see-details'>
              <a className={'link-button'} onClick={toggleDetails}>click for details</a>
              <div onClick={toggleDetails} className={`c-protection-widget__arrow-span ${(modalIsOpen && "expanded") || ""}`}>
                <ArrowDownIcon />
              </div>
            </div>
          }
          {!inlineMode && modalIsOpen &&
            <Modal isOpen={modalIsOpen}
              onBackgroundClick={closePopUp}
              onCloseClick={closePopUp}
              onEscPressed={closePopUp}
              width='auto'
              height='auto'
            >
              <WidgetWrapper className={isLoading ? 'loading' : ''}>
                {Content}
              </WidgetWrapper>
            </Modal>
          }
          {
            inlineMode && <div className={`c-protection-widget__content ${(modalIsOpen && "expanded") || "hidden"}`}>
              {Content}
            </div>
          }
        </div>);
    }
  }

  return <> {renderWidget(status)} </>;
}

function WidgetContent(props: {
  isPolicyActive: boolean | undefined,
  quoteLoader: QuoteLoader,
  topUpValue: BigNumber | undefined,
  desiredCover: string,
  rate: BigNumber | undefined,
  duration: number,
  handleDesiredCoverChanged: (value: string) => boolean,
  handleTopUpValueChanged: (value: string) => boolean,
  handleDurationChanged: (value: number) => boolean,
  policy: Policy | undefined,
  error?: string,
  isPremiumTokenApproved: boolean | null,
  isLoading: boolean,
  handleUnlockToken: () => Promise<any>,
  handleBuyPolicy: () => Promise<any>,
  onPolicyCancelActivate?: (policy: Policy) => void,
  errorDuringActivation?: string | null,
  provider: Web3Provider,
}) {
  const {
    quoteLoader,
    topUpValue,
    desiredCover,
    rate,
    duration,
    handleDesiredCoverChanged,
    handleTopUpValueChanged,
    handleDurationChanged,
    policy,
    error,
    isPremiumTokenApproved,
    isLoading,
    handleUnlockToken,
    handleBuyPolicy,
    errorDuringActivation,
    provider,
    onPolicyCancelActivate,
  } = props;
  return (<>
    <div className={`card-content ${(!policy && 'new') || ""}`}>
    <Box className="widget-box" onClick={(e: any) => e.stopPropagation()}>
      {
        <Fragment>
          <StyledCheckbox>
              {
                !policy && <>
                  <ActivateProtectionView
                    capitalTokenInfo={quoteLoader.capitalToken}
                    insuredTokenInfo={quoteLoader.insuredToken}
                    premiumTokenInfo={quoteLoader.premiumToken}
                    topUpValue={topUpValue}
                    desiredCover={desiredCover}
                    handleDesiredCoverChanged={handleDesiredCoverChanged}
                    handleTopUpValueChanged={handleTopUpValueChanged}
                    handleDurationChanged={handleDurationChanged}
                    minDeposit={quoteLoader.minDeposit}
                    rate={rate?.toFixed(2, BigNumber.ROUND_UP)}
                    monthPrice={quoteLoader.monthlyQuote}
                    duration={duration ?? 0}
                    referralBonus={quoteLoader.bonus}
                    totalDeposit={quoteLoader.quote}
                  />
                <Footer>
                <div className='button-block'>
                <Button disabled={isPremiumTokenApproved || isPremiumTokenApproved === null || isLoading} onClick={handleUnlockToken} className="unlock-button">Unlock {quoteLoader.premiumToken?.symbol}</Button>
                {
                  !policy &&
                  <Button type='button' disabled={!isPremiumTokenApproved || isLoading} onClick={handleBuyPolicy}>Activate &amp; top-up {quoteLoader.quote && quoteLoader.premiumToken && `${toSuffixFormat(quoteLoader.quote, 2, BigNumber.ROUND_UP)} ${quoteLoader.premiumToken.symbol}`}</Button>
                }
                </div>
                {((errorDuringActivation || error) &&
                  <LabelText className='error'>{errorDuringActivation || error}</LabelText>) || <div>&nbsp;</div>
                }
              </Footer>
              </>
              }
              {
                policy &&
                  <PolicyView
                  policy={policy}
                  quoteLoader={quoteLoader}
                  desiredCover={desiredCover}
                  topUpValue={topUpValue}
                  rate={rate}
                  handleDesiredCoverChanged={() => null }
                  handleTopUpValueChanged={() => null }
                  provider={provider}
                  onPolicyCancelActivate={onPolicyCancelActivate}
                />
              }
          </StyledCheckbox>
        </Fragment>
      }
    </Box>
    </div>
  </>);
}
