import { useRouter } from 'next/router'
import { Checkbox, Form, Input, InputUserSuggest, SubmitButton } from '@/components/form'
import Link from 'next/link'
import Button from 'react-bootstrap/Button'
import { gql, useMutation, useQuery } from '@apollo/client'
import Qr, { QrSkeleton } from '@/components/qr'
import { CenterLayout } from '@/components/layout'
import InputGroup from 'react-bootstrap/InputGroup'
import { WithdrawlSkeleton } from '@/pages/withdrawals/[id]'
import { useMe } from '@/components/me'
import { useEffect, useState } from 'react'
import { requestProvider } from 'webln'
import Alert from 'react-bootstrap/Alert'
import { CREATE_WITHDRAWL, SEND_TO_LNADDR } from '@/fragments/wallet'
import { getGetServerSideProps } from '@/api/ssrApollo'
import { amountSchema, lnAddrSchema, withdrawlSchema } from '@/lib/validate'
import Nav from 'react-bootstrap/Nav'
import { BALANCE_LIMIT_MSATS, FAST_POLL_INTERVAL, SSR } from '@/lib/constants'
import { msatsToSats, numWithUnits } from '@/lib/format'
import styles from '@/components/user-header.module.css'
import HiddenWalletSummary from '@/components/hidden-wallet-summary'
import AccordianItem from '@/components/accordian-item'
import { lnAddrOptions } from '@/lib/lnurl'
import useDebounceCallback from '@/components/use-debounce-callback'
import { QrScanner } from '@yudiel/react-qr-scanner'
import CameraIcon from '@/svgs/camera-line.svg'
import { useShowModal } from '@/components/modal'
import { useField } from 'formik'
import { useToast } from '@/components/toast'
import { WalletLimitBanner } from '@/components/banners'
import Plug from '@/svgs/plug.svg'
import { decode } from 'bolt11'

export const getServerSideProps = getGetServerSideProps({ authRequired: true })

export default function Wallet () {
  const router = useRouter()

  if (router.query.type === 'fund') {
    return (
      <CenterLayout>
        <FundForm />
      </CenterLayout>
    )
  } else if (router.query.type?.includes('withdraw')) {
    return (
      <CenterLayout>
        <WithdrawalForm />
      </CenterLayout>
    )
  } else {
    return (
      <CenterLayout>
        <YouHaveSats />
        <WalletLimitBanner />
        <WalletForm />
        <WalletHistory />
      </CenterLayout>
    )
  }
}

function YouHaveSats () {
  const me = useMe()
  const limitReached = me?.privates?.sats >= msatsToSats(BALANCE_LIMIT_MSATS)
  return (
    <h2 className={`${me ? 'visible' : 'invisible'} ${limitReached ? 'text-warning' : 'text-success'}`}>
      you have{' '}
      <span className='text-monospace'>{me && (
        me.privates?.hideWalletBalance
          ? <HiddenWalletSummary />
          : numWithUnits(me.privates?.sats, { abbreviate: false, format: true })
      )}
      </span>
    </h2>
  )
}

function WalletHistory () {
  return (
    <div className='d-flex flex-column text-center'>
      <div>
        <Link href='/satistics?inc=invoice,withdrawal' className='text-muted fw-bold text-underline'>
          wallet history
        </Link>
      </div>
    </div>
  )
}

export function WalletForm () {
  return (
    <div className='align-items-center text-center pt-5 pb-4'>
      <Link href='/wallet?type=fund'>
        <Button variant='success'>fund</Button>
      </Link>
      <span className='mx-3 fw-bold text-muted'>or</span>
      <Link href='/wallet?type=withdraw'>
        <Button variant='success'>withdraw</Button>
      </Link>
      <div className='mt-5'>
        <Link href='/settings/wallets'>
          <Button variant='info'>attach wallets <Plug className='fill-white ms-1' width={16} height={16} /></Button>
        </Link>
      </div>
    </div>
  )
}

export function FundForm () {
  const me = useMe()
  const [showAlert, setShowAlert] = useState(true)
  const router = useRouter()
  const [createInvoice, { called, error }] = useMutation(gql`
    mutation createInvoice($amount: Int!) {
      createInvoice(amount: $amount) {
        id
      }
    }`)

  useEffect(() => {
    setShowAlert(!window.localStorage.getItem('hideLnAddrAlert'))
  }, [])

  if (called && !error) {
    return <QrSkeleton description status='generating' bolt11Info />
  }

  return (
    <>
      <YouHaveSats />
      <WalletLimitBanner />
      <div className='w-100 py-5'>
        {me && showAlert &&
          <Alert
            variant='success' dismissible onClose={() => {
              window.localStorage.setItem('hideLnAddrAlert', 'yep')
              setShowAlert(false)
            }}
          >
            You can also fund your account via lightning address with <strong>{`${me.name}@stacker.news`}</strong>
          </Alert>}
        <Form
          initial={{
            amount: 1000
          }}
          schema={amountSchema}
          onSubmit={async ({ amount }) => {
            const { data } = await createInvoice({ variables: { amount: Number(amount) } })
            router.push(`/invoices/${data.createInvoice.id}`)
          }}
        >
          <Input
            label='amount'
            name='amount'
            required
            autoFocus
            append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
          />
          <SubmitButton variant='success' className='mt-2'>generate invoice</SubmitButton>
        </Form>
      </div>
      <WalletHistory />
    </>
  )
}

export function WithdrawalForm () {
  const router = useRouter()

  return (
    <div className='w-100 d-flex flex-column align-items-center py-5'>
      <YouHaveSats />
      <Nav
        className={styles.nav}
        activeKey={router.query.type}
      >
        <Nav.Item>
          <Link href='/wallet?type=withdraw' passHref legacyBehavior>
            <Nav.Link eventKey='withdraw'>invoice</Nav.Link>
          </Link>
        </Nav.Item>
        <Nav.Item>
          <Link href='/wallet?type=lnurl-withdraw' passHref legacyBehavior>
            <Nav.Link eventKey='lnurl-withdraw'>QR code</Nav.Link>
          </Link>
        </Nav.Item>
        <Nav.Item>
          <Link href='/wallet?type=lnaddr-withdraw' passHref legacyBehavior>
            <Nav.Link eventKey='lnaddr-withdraw'>lightning address</Nav.Link>
          </Link>
        </Nav.Item>
      </Nav>
      <SelectedWithdrawalForm />
    </div>
  )
}

export function SelectedWithdrawalForm () {
  const router = useRouter()

  switch (router.query.type) {
    case 'withdraw':
      return <InvWithdrawal />
    case 'lnurl-withdraw':
      return <LnWithdrawal />
    case 'lnaddr-withdraw':
      return <LnAddrWithdrawal />
  }
}

export function InvWithdrawal () {
  const router = useRouter()
  const me = useMe()

  const [createWithdrawl, { called, error }] = useMutation(CREATE_WITHDRAWL)

  const maxFeeDefault = me?.privates?.withdrawMaxFeeDefault

  useEffect(() => {
    async function effect () {
      try {
        const provider = await requestProvider()
        const { paymentRequest: invoice } = await provider.makeInvoice({
          defaultMemo: `Withdrawal for @${me.name} on SN`,
          maximumAmount: Math.max(me.privates?.sats - maxFeeDefault, 0)
        })
        const { data } = await createWithdrawl({ variables: { invoice, maxFee: maxFeeDefault } })
        router.push(`/withdrawals/${data.createWithdrawl.id}`)
      } catch (e) {
        console.log(e.message)
      }
    }
    effect()
  }, [])

  if (called && !error) {
    return <WithdrawlSkeleton status='sending' />
  }

  return (
    <>
      <Form
        autoComplete='off'
        initial={{
          invoice: '',
          maxFee: maxFeeDefault
        }}
        schema={withdrawlSchema}
        onSubmit={async ({ invoice, maxFee }) => {
          const { data } = await createWithdrawl({ variables: { invoice, maxFee: Number(maxFee) } })
          router.push(`/withdrawals/${data.createWithdrawl.id}`)
        }}
      >
        <Input
          label='invoice'
          name='invoice'
          required
          autoFocus
          clear
          append={<InvoiceScanner fieldName='invoice' />}
        />
        <Input
          label='max fee'
          name='maxFee'
          required
          append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
        />
        <SubmitButton variant='success' className='mt-2'>withdraw</SubmitButton>
      </Form>
    </>
  )
}

function InvoiceScanner ({ fieldName }) {
  const showModal = useShowModal()
  const [,, helpers] = useField(fieldName)
  const toaster = useToast()
  return (
    <InputGroup.Text
      style={{ cursor: 'pointer' }}
      onClick={() => {
        showModal(onClose => {
          return (
            <QrScanner
              onDecode={(result) => {
                if (result.split('lightning=')[1]) {
                  helpers.setValue(result.split('lightning=')[1].split(/[&?]/)[0].toLowerCase())
                } else if (decode(result.replace(/^lightning:/, ''))) {
                  helpers.setValue(result.replace(/^lightning:/, '').toLowerCase())
                } else {
                  throw new Error('Not a proper lightning payment request')
                }
                onClose()
              }}
              onError={(error) => {
                if (error instanceof DOMException) {
                  console.log(error)
                } else {
                  toaster.danger('qr scan: ' + error?.message || error?.toString?.())
                }
                onClose()
              }}
            />
          )
        })
      }}
    >
      <CameraIcon
        height={20} width={20} fill='var(--bs-body-color)'
      />
    </InputGroup.Text>
  )
}

function LnQRWith ({ k1, encodedUrl }) {
  const router = useRouter()
  const query = gql`
  {
    lnWith(k1: "${k1}") {
      withdrawalId
      k1
    }
  }`
  const { data } = useQuery(query, SSR ? {} : { pollInterval: FAST_POLL_INTERVAL, nextFetchPolicy: 'cache-and-network' })

  if (data?.lnWith?.withdrawalId) {
    router.push(`/withdrawals/${data.lnWith.withdrawalId}`)
  }

  return <Qr value={encodedUrl} status='waiting for you' />
}

export function LnWithdrawal () {
  // query for challenge
  const [createWith, { data, error }] = useMutation(gql`
    mutation createWith {
      createWith {
        k1
        encodedUrl
      }
    }`)
  const toaster = useToast()

  useEffect(() => {
    createWith().catch(e => {
      toaster.danger('withdrawal creation: ' + e?.message || e?.toString?.())
    })
  }, [createWith, toaster])

  if (error) return <QrSkeleton status='error' />

  if (!data) {
    return <QrSkeleton status='generating' />
  }

  return <LnQRWith {...data.createWith} />
}

export function LnAddrWithdrawal () {
  const me = useMe()
  const router = useRouter()
  const [sendToLnAddr, { called, error }] = useMutation(SEND_TO_LNADDR)
  const defaultOptions = { min: 1 }
  const [addrOptions, setAddrOptions] = useState(defaultOptions)
  const [formSchema, setFormSchema] = useState(lnAddrSchema())
  const maxFeeDefault = me?.privates?.withdrawMaxFeeDefault

  const onAddrChange = useDebounceCallback(async (formik, e) => {
    if (!e?.target?.value) {
      setAddrOptions(defaultOptions)
      setFormSchema(lnAddrSchema())
      return
    }

    let options
    try {
      options = await lnAddrOptions(e.target.value)
      setAddrOptions(options)
      setFormSchema(lnAddrSchema(options))
    } catch (e) {
      console.log(e)
      setAddrOptions(defaultOptions)
      setFormSchema(lnAddrSchema())
    }
  }, 500, [setAddrOptions, setFormSchema])

  return (
    <>
      {called && !error && <WithdrawlSkeleton status='sending' />}
      <Form
        // hide/show instead of add/remove from react tree to avoid re-initializing the form state on error
        style={{ display: !(called && !error) ? 'block' : 'none' }}
        initial={{
          addr: '',
          amount: 1,
          maxFee: maxFeeDefault,
          comment: '',
          identifier: false,
          name: '',
          email: ''
        }}
        schema={formSchema}
        onSubmit={async ({ amount, maxFee, ...values }) => {
          const { data } = await sendToLnAddr({
            variables: {
              amount: Number(amount),
              maxFee: Number(maxFee),
              ...values
            }
          })
          router.push(`/withdrawals/${data.sendToLnAddr.id}`)
        }}
      >
        <InputUserSuggest
          label='lightning address'
          name='addr'
          required
          autoFocus
          onChange={onAddrChange}
          transformUser={user => ({ ...user, name: `${user.name}@stacker.news` })}
          selectWithTab
          filterUsers={(query) => {
            const [, domain] = query.split('@')
            return !domain || 'stacker.news'.startsWith(domain)
          }}
        />
        <Input
          label='amount'
          name='amount'
          type='number'
          step={10}
          required
          min={addrOptions.min}
          max={addrOptions.max}
          append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
        />
        <Input
          label='max fee'
          name='maxFee'
          type='number'
          step={10}
          required
          append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
        />
        {(addrOptions?.commentAllowed || addrOptions?.payerData) &&
          <div className='my-3 border border-3 rounded'>
            <div className='p-3'>
              <AccordianItem
                show
                header={<div style={{ fontWeight: 'bold', fontSize: '92%' }}>attach</div>}
                body={
                  <>
                    {addrOptions.commentAllowed &&
                      <Input
                        as='textarea'
                        label={<>comment <small className='text-muted ms-2'>optional</small></>}
                        name='comment'
                        maxLength={addrOptions.commentAllowed}
                      />}
                    {addrOptions.payerData?.identifier &&
                      <Checkbox
                        name='identifier'
                        required={addrOptions.payerData.identifier.mandatory}
                        label={
                          <>your {me?.name}@stacker.news identifier
                            {!addrOptions.payerData.identifier.mandatory &&
                              <>{' '}<small className='text-muted ms-2'>optional</small></>}
                          </>
}
                      />}
                    {addrOptions.payerData?.name &&
                      <Input
                        name='name'
                        required={addrOptions.payerData.name.mandatory}
                        label={
                          <>name{!addrOptions.payerData.name.mandatory &&
                            <>{' '}<small className='text-muted ms-2'>optional</small></>}
                          </>
}
                      />}
                    {addrOptions.payerData?.email &&
                      <Input
                        name='email'
                        required={addrOptions.payerData.email.mandatory}
                        label={
                          <>
                            email{!addrOptions.payerData.email.mandatory &&
                              <>{' '}<small className='text-muted ms-2'>optional</small></>}
                          </>
}
                      />}
                  </>
                }
              />
            </div>
          </div>}
        <SubmitButton variant='success' className='mt-2'>send</SubmitButton>
      </Form>
    </>
  )
}