import React, {useState, Fragment, useEffect} from "react";
import {
  Box,
  Footer,
  StyledCheckbox,
  Title,
  Button,
  LabelText,
  Spinner,
} from "../../components/ui/Widget";
import {AtomicaLightLogo} from "../../components/ui/AtomicaLightLogo";
import "../../utils/configureFormats";
import {Environment} from "rm-sdk/build/types";
import BigNumber from "bignumber.js";
import {Web3Provider} from "@ethersproject/providers";
import {QuoteLoader} from "../../helpers/quote-loader";
import {
  checkProviderState,
  getNetworkParams,
  processErrorMessage,
} from "../../utils/commonUtils";
import {toSuffixFormat} from "../../utils/configureFormats";
;
import "./styles.scss";
import {ERRORS} from "../../helpers/constants";
import {CustomActivateProtectionView} from "./CustomActivateProtectionView";

interface IProps {
  provider: Web3Provider
  desiredLoanAmount?: string
  marketId: string
  environment: Environment | string
  className?: string
  referrer?: string
  defaultDuration?: number
  onRequestSuccess?: (policyId: string) => void
  minDuration?: number
  maxDuration?: number
  hideLogo?: boolean
}

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

export function CustomRMWidget({
  provider,
  desiredLoanAmount,
  marketId,
  environment,
  className,
  referrer,
  defaultDuration = 30,
  onRequestSuccess = () => {},
  minDuration,
  maxDuration,
  hideLogo = false,
}: IProps) {
  const [quoteLoader, setQuoteLoader] = useState(new QuoteLoader(environment))

  const [status, setStatus] = useState(WidgetStatus.Initializing)
  const [desiredCover, setDesiredCover] = useState(desiredLoanAmount || "0")
  const [total, setTotal] = useState<BigNumber>()
  const [topUpValue, setTopUpValue] = useState<BigNumber>()
  const [duration, setDuration] = useState(defaultDuration)
  const [isLoading, setLoading] = useState(true)

  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 [chainId, setChainId] = useState<number>()

  useEffect(() => {
    providerChanged()
  }, [provider])

  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])

  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) {
    try {
      setLoading(true)

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

      setLoading(false)

      if (chainId && quoteLoader.sdk.envConfig?.chainId !== chainId) {
        setError(ERRORS.WRONG_NETWORK)
      }

      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)
    }
  }

  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,
        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()

          onRequestSuccess?.(policyId)
        }

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

        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)

    setStatus(r ? WidgetStatus.Protected : WidgetStatus.NoProtection)

    setLoading(false)
  }

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

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

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

      return true
    }

    return false
  }

  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 BuyNewProtection = (
    <WidgetContent
      quoteLoader={quoteLoader}
      topUpValue={topUpValue}
      desiredCover={desiredCover}
      rate={rate}
      duration={duration}
      handleDesiredCoverChanged={handleDesiredCoverChanged}
      handleTopUpValueChanged={handleTopUpValueChanged}
      handleDurationChanged={handleDurationChanged}
      error={error}
      isPremiumTokenApproved={isPremiumTokenApproved}
      isLoading={isLoading}
      handleUnlockToken={handleUnlockToken}
      handleBuyPolicy={handleBuyPolicy}
      errorDuringActivation={errorDuringActivation}
      minDuration={minDuration}
      maxDuration={maxDuration}
    />
  )

  const Content = (
    <>
      {!hideLogo && (
        <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>Request loan with Atomica</p>
        </Title>
      )}

      <div className="c-request-loan-widget__widget-content-container">
        <div className="c-request-loan-widget__buy-protection-wrapper">
          {BuyNewProtection}
        </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-request-loan-widget ${
              className === "dark-theme" ? className : "light-theme"
            }`}
          >
            <div className="c-request-loan-widget__content expanded">
              {Content}
            </div>
          </div>
        )
    }
  }

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

function WidgetContent(props: {
  quoteLoader: QuoteLoader
  topUpValue: BigNumber | undefined
  desiredCover: string
  rate: BigNumber | undefined
  duration: number
  handleDesiredCoverChanged: (value: string) => boolean
  handleTopUpValueChanged: (value: string) => boolean
  handleDurationChanged: (value: number) => boolean
  error?: string
  isPremiumTokenApproved: boolean | null
  isLoading: boolean
  handleUnlockToken: () => Promise<any>
  handleBuyPolicy: () => Promise<any>
  errorDuringActivation?: string | null
  minDuration?: number
  maxDuration?: number
}) {
  const {
    quoteLoader,
    topUpValue,
    desiredCover,
    rate,
    duration,
    handleDesiredCoverChanged,
    handleTopUpValueChanged,
    handleDurationChanged,
    error,
    isPremiumTokenApproved,
    isLoading,
    handleUnlockToken,
    handleBuyPolicy,
    errorDuringActivation,
    minDuration,
    maxDuration,
  } = props
  return (
    <Box className="widget-box" onClick={(e: any) => e.stopPropagation()}>
      {
        <Fragment>
          <StyledCheckbox>
            <>
              <CustomActivateProtectionView
                capitalTokenInfo={quoteLoader.capitalToken}
                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}
                maxDuration={maxDuration}
                minDuration={minDuration}
              />
              <Footer>
                <div className="button-block">
                  <Button
                    disabled={
                      isPremiumTokenApproved ||
                      isPremiumTokenApproved === null ||
                      isLoading
                    }
                    onClick={handleUnlockToken}
                    className="unlock-button"
                  >
                    Unlock {quoteLoader.premiumToken?.symbol}
                  </Button>

                  <Button
                    type="button"
                    disabled={!isPremiumTokenApproved || isLoading}
                    onClick={handleBuyPolicy}
                  >
                    Request &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>
            </>
          </StyledCheckbox>
        </Fragment>
      }
    </Box>
  )
}
