import { Button } from '@/components/ui/button'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle
} from '@/components/ui/dialog'
import {
  Drawer,
  DrawerContent,
  DrawerHeader,
  DrawerOverlay,
  DrawerTitle
} from '@/components/ui/drawer'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useNostr } from '@/providers/NostrProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { useZap } from '@/providers/ZapProvider'
import lightning from '@/services/lightning.service'
import stuffStatsService from '@/services/stuff-stats.service'
import { Loader } from 'lucide-react'
import { NostrEvent } from 'nostr-tools'
import { Dispatch, SetStateAction, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import UserAvatar from '../UserAvatar'
import Username from '../Username'

export default function ZapDialog({
  open,
  setOpen,
  pubkey,
  event,
  defaultAmount,
  defaultComment
}: {
  open: boolean
  setOpen: Dispatch<SetStateAction<boolean>>
  pubkey: string
  event?: NostrEvent
  defaultAmount?: number
  defaultComment?: string
}) {
  const { t } = useTranslation()
  const { isSmallScreen } = useScreenSize()
  const drawerContentRef = useRef<HTMLDivElement | null>(null)

  useEffect(() => {
    const handleResize = () => {
      if (drawerContentRef.current) {
        drawerContentRef.current.style.setProperty('bottom', `env(safe-area-inset-bottom)`)
      }
    }

    if (window.visualViewport) {
      window.visualViewport.addEventListener('resize', handleResize)
      handleResize() // Initial call in case the keyboard is already open
    }

    return () => {
      if (window.visualViewport) {
        window.visualViewport.removeEventListener('resize', handleResize)
      }
    }
  }, [])

  if (isSmallScreen) {
    return (
      <Drawer open={open} onOpenChange={setOpen}>
        <DrawerOverlay onClick={() => setOpen(false)} />
        <DrawerContent
          hideOverlay
          onOpenAutoFocus={(e) => e.preventDefault()}
          ref={drawerContentRef}
          className="mb-4 flex flex-col gap-4 px-4"
        >
          <DrawerHeader>
            <DrawerTitle className="flex items-center gap-2">
              <div className="shrink-0">{t('Zap to')}</div>
              <UserAvatar size="small" userId={pubkey} />
              <Username userId={pubkey} className="h-5 w-0 flex-1 truncate text-start" />
            </DrawerTitle>
            <DialogDescription></DialogDescription>
          </DrawerHeader>
          <ZapDialogContent
            open={open}
            setOpen={setOpen}
            recipient={pubkey}
            event={event}
            defaultAmount={defaultAmount}
            defaultComment={defaultComment}
          />
        </DrawerContent>
      </Drawer>
    )
  }

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogContent onOpenAutoFocus={(e) => e.preventDefault()}>
        <DialogHeader>
          <DialogTitle className="flex items-center gap-2">
            <div className="shrink-0">{t('Zap to')}</div>
            <UserAvatar size="small" userId={pubkey} />
            <Username userId={pubkey} className="h-5 max-w-fit flex-1 truncate text-start" />
          </DialogTitle>
        </DialogHeader>
        <ZapDialogContent
          open={open}
          setOpen={setOpen}
          recipient={pubkey}
          event={event}
          defaultAmount={defaultAmount}
          defaultComment={defaultComment}
        />
      </DialogContent>
    </Dialog>
  )
}

function ZapDialogContent({
  setOpen,
  recipient,
  event,
  defaultAmount,
  defaultComment
}: {
  open: boolean
  setOpen: Dispatch<SetStateAction<boolean>>
  recipient: string
  event?: NostrEvent
  defaultAmount?: number
  defaultComment?: string
}) {
  const { t, i18n } = useTranslation()
  const { pubkey } = useNostr()
  const { defaultZapSats, defaultZapComment } = useZap()
  const [sats, setSats] = useState(defaultAmount ?? defaultZapSats)
  const [comment, setComment] = useState(defaultComment ?? defaultZapComment)
  const isSelfZap = useMemo(() => pubkey === recipient, [pubkey, recipient])
  const [zapping, setZapping] = useState(false)
  const presetAmounts = useMemo(() => {
    if (i18n.language.startsWith('zh')) {
      return [
        { display: '21', val: 21 },
        { display: '66', val: 66 },
        { display: '210', val: 210 },
        { display: '666', val: 666 },
        { display: '1k', val: 1000 },
        { display: '2.1k', val: 2100 },
        { display: '6.6k', val: 6666 },
        { display: '10k', val: 10000 },
        { display: '21k', val: 21000 },
        { display: '66k', val: 66666 },
        { display: '100k', val: 100000 },
        { display: '210k', val: 210000 }
      ]
    }

    return [
      { display: '21', val: 21 },
      { display: '42', val: 42 },
      { display: '210', val: 210 },
      { display: '420', val: 420 },
      { display: '1k', val: 1000 },
      { display: '2.1k', val: 2100 },
      { display: '4.2k', val: 4200 },
      { display: '10k', val: 10000 },
      { display: '21k', val: 21000 },
      { display: '42k', val: 42000 },
      { display: '100k', val: 100000 },
      { display: '210k', val: 210000 }
    ]
  }, [i18n.language])

  const handleZap = async () => {
    try {
      if (!pubkey) {
        throw new Error('You need to be logged in to zap')
      }
      setZapping(true)
      const zapResult = await lightning.zap(pubkey, event ?? recipient, sats, comment, () =>
        setOpen(false)
      )
      // user canceled
      if (!zapResult) {
        return
      }
      if (event) {
        stuffStatsService.addZap(pubkey, event.id, zapResult.invoice, sats, comment)
      }
    } catch (error) {
      toast.error(`${t('Zap failed')}: ${(error as Error).message}`)
    } finally {
      setZapping(false)
    }
  }

  return (
    <>
      {/* Sats slider or input */}
      <div className="flex flex-col items-center">
        <div className="flex w-full justify-center">
          <input
            id="sats"
            value={sats}
            onChange={(e) => {
              setSats((pre) => {
                if (e.target.value === '') {
                  return 0
                }
                let num = parseInt(e.target.value, 10)
                if (isNaN(num) || num < 0) {
                  num = pre
                }
                return num
              })
            }}
            onFocus={(e) => {
              requestAnimationFrame(() => {
                const val = e.target.value
                e.target.setSelectionRange(val.length, val.length)
              })
            }}
            className="w-full bg-transparent p-0 text-center text-6xl font-bold focus-visible:outline-none"
          />
        </div>
        <Label htmlFor="sats">{t('Sats')}</Label>
      </div>

      {/* Self-zap easter egg warning */}
      {isSelfZap && (
        <div className="rounded-md border border-yellow-200 bg-yellow-50 px-4 py-2 text-center text-sm text-yellow-600 dark:border-yellow-900 dark:bg-yellow-950/30 dark:text-yellow-400">
          {t('selfZapWarning')}
        </div>
      )}

      {/* Preset sats buttons */}
      <div className="grid grid-cols-6 gap-2">
        {presetAmounts.map(({ display, val }) => (
          <Button variant="secondary" key={val} onClick={() => setSats(val)}>
            {display}
          </Button>
        ))}
      </div>

      {/* Comment input */}
      <div>
        <Label htmlFor="comment">{t('zapComment')}</Label>
        <Input id="comment" value={comment} onChange={(e) => setComment(e.target.value)} />
      </div>

      <Button onClick={handleZap}>
        {zapping && <Loader className="animate-spin" />} {t('Zap n sats', { n: sats })}
      </Button>
    </>
  )
}
