import { ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'

import { BZZ, DAI, Duration, PostageBatch, RedundancyLevel, Size, Utils } from '@ethersphere/bee-js'
import './InitialModal.scss'
import { CustomDropdown } from '../CustomDropdown/CustomDropdown'
import { Button } from '../Button/Button'
import { calculateStampCapacityMetrics, fmFetchCost, getUsableStamps, handleCreateDrive } from '../../utils/bee'
import { getExpiryDateByLifetime, safeSetState } from '../../utils/common'
import { erasureCodeMarks } from '../../constants/common'
import { desiredLifetimeOptions } from '../../constants/stamps'
import { Context as SettingsContext } from '../../../../providers/Settings'
import { Context as BeeContext } from '../../../../providers/Bee'

import { FMSlider } from '../Slider/Slider'
import { Context as FMContext } from '../../../../providers/FileManager'
import { ADMIN_STAMP_LABEL } from '@solarpunkltd/file-manager-lib'
import { ProgressBar } from '../ProgressBar/ProgressBar'
import { Tooltip } from '../Tooltip/Tooltip'
import { TOOLTIPS } from '../../constants/tooltips'

interface InitialModalProps {
  resetState: boolean
  handleVisibility: (isVisible: boolean) => void
  handleShowError: (flag: boolean) => void
  setIsCreationInProgress: (isCreating: boolean) => void
}

const minMarkValue = Math.min(...erasureCodeMarks.map(mark => mark.value))
const maxMarkValue = Math.max(...erasureCodeMarks.map(mark => mark.value))

const BATCH_ID_PLACEHOLDER = 'Choose a saved Drive, or leave blank to create a new one'

const createBatchIdOptions = (stamps: PostageBatch[]) => [
  { label: BATCH_ID_PLACEHOLDER, value: -1 },
  ...stamps.map((stamp, index) => {
    const batchId = stamp.batchID.toHex().slice(0, 8)
    const label = `${batchId}${stamp.label ? ` - ${stamp.label}` : ''}`

    return {
      label,
      value: index,
    }
  }),
]

export function InitialModal({
  resetState,
  setIsCreationInProgress,
  handleVisibility,
  handleShowError,
}: InitialModalProps): ReactElement {
  const [isCreateEnabled, setIsCreateEnabled] = useState(false)
  const [isBalanceSufficient, setIsBalanceSufficient] = useState(true)
  const [isxDaiBalanceSufficient, setIsxDaiBalanceSufficient] = useState(true)
  const [capacity, setCapacity] = useState(0)
  const [lifetimeIndex, setLifetimeIndex] = useState(0)
  const [validityEndDate, setValidityEndDate] = useState(new Date())
  const [erasureCodeLevel, setErasureCodeLevel] = useState(RedundancyLevel.OFF)
  const [cost, setCost] = useState('0')
  const [usableStamps, setUsableStamps] = useState<PostageBatch[]>([])
  const [selectedBatch, setSelectedBatch] = useState<PostageBatch | null>(null)
  const [selectedBatchIndex, setSelectedBatchIndex] = useState<number>(-1)

  const { walletBalance } = useContext(BeeContext)
  const { beeApi } = useContext(SettingsContext)
  const { fm } = useContext(FMContext)

  const currentFetch = useRef<Promise<void> | null>(null)
  const isMountedRef = useRef(true)

  useEffect(() => {
    return () => {
      isMountedRef.current = false
    }
  }, [])

  const createAdminDrive = useCallback(async () => {
    setIsCreationInProgress?.(true)
    handleVisibility(false)

    await handleCreateDrive(
      beeApi,
      fm,
      Size.fromBytes(capacity),
      Duration.fromEndDate(validityEndDate),
      ADMIN_STAMP_LABEL,
      false,
      erasureCodeLevel,
      true,
      resetState,
      selectedBatch,
      () => {
        handleVisibility(false)
        setIsCreationInProgress(false)
      }, // onSuccess
      () => {
        handleShowError(true)
        setIsCreationInProgress(false)
      }, // onError
    )
  }, [
    beeApi,
    fm,
    capacity,
    validityEndDate,
    erasureCodeLevel,
    selectedBatch,
    handleVisibility,
    handleShowError,
    setIsCreationInProgress,
    resetState,
  ])

  useEffect(() => {
    const getStamps = async () => {
      const stamps = (await getUsableStamps(beeApi)).filter(s => {
        const { capacityPct } = calculateStampCapacityMetrics(s)

        return capacityPct < 100
      })

      safeSetState(isMountedRef, setUsableStamps)([...stamps])
    }

    if (beeApi) {
      getStamps()
    }
  }, [beeApi])

  useEffect(() => {
    const newSizes = Array.from(Utils.getStampEffectiveBytesBreakpoints(false, erasureCodeLevel).values())

    setCapacity(newSizes[2])
  }, [erasureCodeLevel])

  useEffect(() => {
    if (validityEndDate.getTime() > new Date().getTime()) {
      fmFetchCost(
        capacity,
        validityEndDate,
        false,
        erasureCodeLevel,
        beeApi,
        (cost: BZZ) => {
          setIsBalanceSufficient(true)
          setIsxDaiBalanceSufficient(true)

          if ((walletBalance && cost.gte(walletBalance.bzzBalance)) || !walletBalance) {
            safeSetState(isMountedRef, setIsBalanceSufficient)(false)
          }

          const zeroDAI = DAI.fromDecimalString('0')

          if ((walletBalance && zeroDAI.eq(walletBalance.nativeTokenBalance)) || !walletBalance) {
            safeSetState(isMountedRef, setIsxDaiBalanceSufficient)(false)
          }

          safeSetState(isMountedRef, setCost)(cost.toSignificantDigits(2))
        },
        currentFetch,
      )

      if (lifetimeIndex >= 0) {
        setIsCreateEnabled(true)
      }
    } else {
      setCost('0')
      setIsCreateEnabled(false)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [validityEndDate, beeApi, capacity, lifetimeIndex, walletBalance])

  useEffect(() => {
    setValidityEndDate(getExpiryDateByLifetime(lifetimeIndex))
  }, [lifetimeIndex])

  useEffect(() => {
    if (selectedBatchIndex >= 0 && selectedBatchIndex < usableStamps.length) {
      setSelectedBatch(usableStamps[selectedBatchIndex])
    } else {
      setSelectedBatch(null)
    }
  }, [usableStamps, selectedBatchIndex])

  const { capacityPct, usedSize, totalSize } = useMemo(
    () => calculateStampCapacityMetrics(selectedBatch),
    [selectedBatch],
  )

  const initText = resetState ? 'Resetting' : 'Initializing'
  const createText = resetState ? 'Reset' : 'Create'

  return (
    <div className="fm-initialization-modal-container">
      <div className="fm-modal-window">
        <div className="fm-modal-window-header">Welcome to your Swarm File Manager</div>
        <div>{initText} the File Manager</div>
        {usableStamps.length > 0 && (
          <div className="fm-modal-window-body">
            <div className="fm-modal-window-input-container">
              {/* <label htmlFor="admin-desired-lifetime" className="fm-input-label">
                Link an existing Admin Drive (optional)
              </label>
              <br /> */}
              <CustomDropdown
                id="batch-id-selector"
                options={createBatchIdOptions(usableStamps)}
                value={selectedBatchIndex}
                label="Link an existing Admin Drive (optional)"
                onChange={(index: number) => {
                  setSelectedBatchIndex(index)

                  if (index === -1) {
                    setSelectedBatch(null)
                  }
                }}
                placeholder={BATCH_ID_PLACEHOLDER}
              />
              {selectedBatch && (
                <div className="fm-drive-item-content">
                  <div className="fm-drive-item-capacity">
                    Capacity <ProgressBar value={capacityPct} width="64px" /> {usedSize} / {totalSize}
                  </div>
                  <div className="fm-drive-item-capacity">
                    Expiry date: {selectedBatch.duration.toEndDate().toLocaleDateString()}
                  </div>
                </div>
              )}
            </div>
          </div>
        )}
        {!selectedBatch && (
          <div className="fm-modal-window-body">
            <div className="fm-modal-window-input-container">
              <label htmlFor="admin-desired-lifetime" className="fm-input-label">
                Create a new Admin Drive with desired lifetime: <Tooltip label={TOOLTIPS.ADMIN_DESIRED_LIFETIME} />
              </label>
              <CustomDropdown
                id="admin-desired-lifetime"
                options={desiredLifetimeOptions}
                value={lifetimeIndex}
                onChange={setLifetimeIndex}
                placeholder="Select a value"
              />
            </div>
            <div className="fm-modal-window-input-container">
              <label htmlFor="admin-security-level" className="fm-input-label">
                Security Level <Tooltip label={TOOLTIPS.ADMIN_SECURITY_LEVEL} />
              </label>
              <FMSlider
                id="admin-security-level"
                defaultValue={0}
                marks={erasureCodeMarks}
                onChange={value => setErasureCodeLevel(value)}
                minValue={minMarkValue}
                maxValue={maxMarkValue}
                step={1}
              />
            </div>
            <div className="fm-modal-window-input-container">
              <div className="fm-modal-estimated-cost-container">
                <div className="fm-emphasized-text">Estimated Cost:</div>
                <div>
                  {cost} BZZ {isBalanceSufficient ? '' : '(Insufficient balance)'}
                  {isxDaiBalanceSufficient ? '' : ' (Insufficient xDAI balance)'}
                </div>
                <Tooltip label={TOOLTIPS.ADMIN_ESTIMATED_COST} />
              </div>
              <div>(Based on current network conditions)</div>
            </div>
          </div>
        )}
        <div className="fm-modal-window-footer">
          <Button
            label={selectedBatch ? `${createText} Drive` : `Purchase Stamp & ${createText} Drive`}
            variant="primary"
            disabled={selectedBatch ? false : !isCreateEnabled || !isBalanceSufficient || !isxDaiBalanceSufficient}
            onClick={createAdminDrive}
          />
          <Tooltip
            label={
              selectedBatch
                ? TOOLTIPS.ADMIN_PURCHASE_BUTTON_ALREADY_EXISTED_ADMIN_DRIVE
                : TOOLTIPS.ADMIN_PURCHASE_BUTTON
            }
          />
        </div>
      </div>
    </div>
  )
}
