import { FC, useCallback, useEffect, useMemo, useState } from 'react'
import Container from '../Container'
import Flex from '../Flex'
import Input from '../Input'
import Select from '../Select'
import Text from '../Text'
import {
  SelectOption,
  TransactionState,
  transactionsOptions,
  TxFields,
  getTxFields,
  defaultTransactionType
} from '../../state/transactions'
import { useSnapshot } from 'valtio'
import state from '../../state'
import { streamState } from '../DebugStream'
import { Button } from '..'
import Textarea from '../Textarea'

interface UIProps {
  setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
  state: TransactionState
  estimateFee?: (...arg: any) => Promise<string | undefined>
}

export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) => {
  const { accounts } = useSnapshot(state)
  const { selectedAccount, selectedDestAccount, selectedTransaction, txFields } = txState

  const accountOptions: SelectOption[] = accounts.map(acc => ({
    label: acc.name,
    value: acc.address
  }))

  const destAccountOptions: SelectOption[] = accounts
    .map(acc => ({
      label: acc.name,
      value: acc.address
    }))
    .filter(acc => acc.value !== selectedAccount?.value)

  const [feeLoading, setFeeLoading] = useState(false)

  const resetFields = useCallback(
    (tt: string) => {
      const fields = getTxFields(tt)

      if (fields.Destination !== undefined) {
        setState({ selectedDestAccount: null })
        fields.Destination = ''
      } else {
        fields.Destination = undefined
      }
      return setState({ txFields: fields })
    },
    [setState]
  )

  const handleSetAccount = (acc: SelectOption) => {
    setState({ selectedAccount: acc })
    streamState.selectedAccount = acc
  }

  const handleSetField = useCallback(
    (field: keyof TxFields, value: string, opFields?: TxFields) => {
      const fields = opFields || txFields
      const obj = fields[field]
      setState({
        txFields: {
          ...fields,
          [field]: typeof obj === 'object' ? { ...obj, $value: value } : value
        }
      })
    },
    [setState, txFields]
  )

  const handleEstimateFee = useCallback(
    async (state?: TransactionState, silent?: boolean) => {
      setFeeLoading(true)

      const fee = await estimateFee?.(state, { silent })
      if (fee) handleSetField('Fee', fee, state?.txFields)

      setFeeLoading(false)
    },
    [estimateFee, handleSetField]
  )

  const handleChangeTxType = useCallback(
    (tt: SelectOption) => {
      setState({ selectedTransaction: tt })

      const newState = resetFields(tt.value)

      handleEstimateFee(newState, true)
    },
    [handleEstimateFee, resetFields, setState]
  )

  const switchToJson = () => setState({ viewType: 'json' })

  // default tx
  useEffect(() => {
    if (selectedTransaction?.value) return

    if (defaultTransactionType) {
      handleChangeTxType(defaultTransactionType)
    }
  }, [handleChangeTxType, selectedTransaction?.value])

  const fields = useMemo(
    () => getTxFields(selectedTransaction?.value),
    [selectedTransaction?.value]
  )

  const specialFields = ['TransactionType', 'Account']
  if (fields.Destination !== undefined) {
    specialFields.push('Destination')
  }

  const otherFields = Object.keys(txFields).filter(k => !specialFields.includes(k)) as [
    keyof TxFields
  ]

  return (
    <Container
      css={{
        p: '$3 01',
        fontSize: '$sm',
        height: 'calc(100% - 45px)'
      }}
    >
      <Flex column fluid css={{ height: '100%', overflowY: 'auto', pr: '$1' }}>
        <Flex
          row
          fluid
          css={{
            justifyContent: 'flex-end',
            alignItems: 'center',
            mb: '$3',
            mt: '1px',
            pr: '1px'
          }}
        >
          <Text muted css={{ mr: '$3' }}>
            Transaction type:{' '}
          </Text>
          <Select
            instanceId="transactionsType"
            placeholder="Select transaction type"
            options={transactionsOptions}
            hideSelectedOptions
            css={{ width: '70%' }}
            value={selectedTransaction}
            onChange={(tt: any) => handleChangeTxType(tt)}
          />
        </Flex>
        <Flex
          row
          fluid
          css={{
            justifyContent: 'flex-end',
            alignItems: 'center',
            mb: '$3',
            pr: '1px'
          }}
        >
          <Text muted css={{ mr: '$3' }}>
            Account:{' '}
          </Text>
          <Select
            instanceId="from-account"
            placeholder="Select your account"
            css={{ width: '70%' }}
            options={accountOptions}
            value={selectedAccount}
            onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
          />
        </Flex>
        {fields.Destination !== undefined && (
          <Flex
            row
            fluid
            css={{
              justifyContent: 'flex-end',
              alignItems: 'center',
              mb: '$3',
              pr: '1px'
            }}
          >
            <Text muted css={{ mr: '$3' }}>
              Destination account:{' '}
            </Text>
            <Select
              instanceId="to-account"
              placeholder="Select the destination account"
              css={{ width: '70%' }}
              options={destAccountOptions}
              value={selectedDestAccount}
              isClearable
              onChange={(acc: any) => setState({ selectedDestAccount: acc })}
            />
          </Flex>
        )}
        {otherFields.map(field => {
          let _value = txFields[field]

          let value: string | undefined
          if (typeof _value === 'object') {
            if (_value.$type === 'json' && typeof _value.$value === 'object') {
              value = JSON.stringify(_value.$value, null, 2)
            } else {
              value = _value.$value.toString()
            }
          } else {
            value = _value?.toString()
          }

          const isXrp = typeof _value === 'object' && _value.$type === 'xrp'
          const isJson = typeof _value === 'object' && _value.$type === 'json'
          const isFee = field === 'Fee'
          let rows = isJson ? (value?.match(/\n/gm)?.length || 0) + 1 : undefined
          if (rows && rows > 5) rows = 5
          return (
            <Flex column key={field} css={{ mb: '$2', pr: '1px' }}>
              <Flex
                row
                fluid
                css={{
                  justifyContent: 'flex-end',
                  alignItems: 'center',
                  position: 'relative'
                }}
              >
                <Text muted css={{ mr: '$3' }}>
                  {field + (isXrp ? ' (XRP)' : '')}:{' '}
                </Text>
                {isJson ? (
                  <Textarea
                    rows={rows}
                    value={value}
                    spellCheck={false}
                    onChange={switchToJson}
                    css={{
                      width: '70%',
                      flex: 'inherit',
                      resize: 'vertical'
                    }}
                  />
                ) : (
                  <Input
                    type={isFee ? 'number' : 'text'}
                    value={value}
                    onChange={e => {
                      if (isFee) {
                        const val = e.target.value.replaceAll('.', '').replaceAll(',', '')
                        handleSetField(field, val)
                      } else {
                        handleSetField(field, e.target.value)
                      }
                    }}
                    onKeyPress={
                      isFee
                        ? e => {
                            if (e.key === '.' || e.key === ',') {
                              e.preventDefault()
                            }
                          }
                        : undefined
                    }
                    css={{
                      width: '70%',
                      flex: 'inherit',
                      '-moz-appearance': 'textfield',
                      '&::-webkit-outer-spin-button': {
                        '-webkit-appearance': 'none',
                        margin: 0
                      },
                      '&::-webkit-inner-spin-button ': {
                        '-webkit-appearance': 'none',
                        margin: 0
                      }
                    }}
                  />
                )}
                {isFee && (
                  <Button
                    size="xs"
                    variant="primary"
                    outline
                    disabled={txState.txIsDisabled}
                    isDisabled={txState.txIsDisabled}
                    isLoading={feeLoading}
                    css={{
                      position: 'absolute',
                      right: '$2',
                      fontSize: '$xs',
                      cursor: 'pointer',
                      alignContent: 'center',
                      display: 'flex'
                    }}
                    onClick={() => handleEstimateFee()}
                  >
                    Suggest
                  </Button>
                )}
              </Flex>
            </Flex>
          )
        })}
      </Flex>
    </Container>
  )
}
