import { useState, useEffect, useMemo } from 'react'
import { useQuery } from '@apollo/client'
import Comment, { CommentSkeleton } from './comment'
import Item from './item'
import ItemJob from './item-job'
import { NOTIFICATIONS } from '../fragments/notifications'
import MoreFooter from './more-footer'
import Invite from './invite'
import { ignoreClick } from '../lib/clicks'
import { dayMonthYear, timeSince } from '../lib/time'
import Link from 'next/link'
import Check from '../svgs/check-double-line.svg'
import HandCoin from '../svgs/hand-coin-fill.svg'
import { LOST_BLURBS, FOUND_BLURBS } from '../lib/constants'
import CowboyHatIcon from '../svgs/cowboy.svg'
import BaldIcon from '../svgs/bald.svg'
import { RootProvider } from './root'
import Alert from 'react-bootstrap/Alert'
import styles from './notifications.module.css'
import { useServiceWorker } from './serviceworker'
import { Checkbox, Form } from './form'
import { useRouter } from 'next/router'
import { useData } from './use-data'
import { nostrZapDetails } from '../lib/nostr'
import Text from './text'
import NostrIcon from '../svgs/nostr.svg'
import { numWithUnits } from '../lib/format'
import BountyIcon from '../svgs/bounty-bag.svg'
import { LongCountdown } from './countdown'
import { nextBillingWithGrace } from '../lib/territory'
import { commentSubTreeRootId } from '../lib/item'

function Notification ({ n, fresh }) {
  const type = n.__typename

  return (
    <NotificationLayout nid={nid(n)} {...defaultOnClick(n)} fresh={fresh}>
      {
        (type === 'Earn' && <EarnNotification n={n} />) ||
        (type === 'Revenue' && <RevenueNotification n={n} />) ||
        (type === 'Invitification' && <Invitification n={n} />) ||
        (type === 'InvoicePaid' && (n.invoice.nostr ? <NostrZap n={n} /> : <InvoicePaid n={n} />)) ||
        (type === 'Referral' && <Referral n={n} />) ||
        (type === 'Streak' && <Streak n={n} />) ||
        (type === 'Votification' && <Votification n={n} />) ||
        (type === 'ForwardedVotification' && <ForwardedVotification n={n} />) ||
        (type === 'Mention' && <Mention n={n} />) ||
        (type === 'JobChanged' && <JobChanged n={n} />) ||
        (type === 'Reply' && <Reply n={n} />) ||
        (type === 'SubStatus' && <SubStatus n={n} />) ||
        (type === 'FollowActivity' && <FollowActivity n={n} />) ||
        (type === 'TerritoryPost' && <TerritoryPost n={n} />)
      }
    </NotificationLayout>
  )
}

function NotificationLayout ({ children, nid, href, as, fresh }) {
  const router = useRouter()
  if (!href) return <div className={fresh ? styles.fresh : ''}>{children}</div>
  return (
    <div
      className={
        `clickToContext ${fresh ? styles.fresh : ''} ${router?.query?.nid === nid ? 'outline-it' : ''}`
      }
      onClick={async (e) => {
        if (ignoreClick(e)) return
        nid && await router.replace({
          pathname: router.pathname,
          query: {
            ...router.query,
            nid
          }
        }, router.asPath, { ...router.options, shallow: true })
        router.push(href, as)
      }}
    >
      {children}
    </div>
  )
}

const defaultOnClick = n => {
  const type = n.__typename
  if (type === 'Earn') {
    let href = '/rewards/'
    if (n.minSortTime !== n.sortTime) {
      href += `${dayMonthYear(new Date(n.minSortTime))}/`
    }
    href += dayMonthYear(new Date(n.sortTime))
    return { href }
  }
  if (type === 'Revenue') return { href: `/~${n.subName}` }
  if (type === 'SubStatus') return { href: `/~${n.sub.name}` }
  if (type === 'Invitification') return { href: '/invites' }
  if (type === 'InvoicePaid') return { href: `/invoices/${n.invoice.id}` }
  if (type === 'Referral') return { href: '/referrals/month' }
  if (type === 'Streak') return {}

  // Votification, Mention, JobChanged, Reply all have item
  if (!n.item.title) {
    const rootId = commentSubTreeRootId(n.item)
    return {
      href: {
        pathname: '/items/[id]',
        query: { id: rootId, commentId: n.item.id }
      },
      as: `/items/${rootId}`
    }
  } else {
    return {
      href: {
        pathname: '/items/[id]',
        query: { id: n.item.id }
      },
      as: `/items/${n.item.id}`
    }
  }
}

function Streak ({ n }) {
  function blurb (n) {
    const index = Number(n.id) % Math.min(FOUND_BLURBS.length, LOST_BLURBS.length)
    if (n.days) {
      return `After ${numWithUnits(n.days, {
        abbreviate: false,
        unitSingular: 'day',
         unitPlural: 'days'
      })}, ` + LOST_BLURBS[index]
    }

    return FOUND_BLURBS[index]
  }

  return (
    <div className='d-flex ms-2 py-1'>
      <div style={{ fontSize: '2rem' }}>{n.days ? <BaldIcon className='fill-grey' height={40} width={40} /> : <CowboyHatIcon className='fill-grey' height={40} width={40} />}</div>
      <div className='ms-1 p-1'>
        <span className='fw-bold'>you {n.days ? 'lost your' : 'found a'} cowboy hat</span>
        <div><small style={{ lineHeight: '140%', display: 'inline-block' }}>{blurb(n)}</small></div>
      </div>
    </div>
  )
}

function EarnNotification ({ n }) {
  const time = n.minSortTime === n.sortTime ? dayMonthYear(new Date(n.minSortTime)) : `${dayMonthYear(new Date(n.minSortTime))} to ${dayMonthYear(new Date(n.sortTime))}`

  return (
    <div className='d-flex ms-2 py-1'>
      <HandCoin className='align-self-center fill-boost mx-1' width={24} height={24} style={{ flex: '0 0 24px', transform: 'rotateY(180deg)' }} />
      <div className='ms-2'>
        <div className='fw-bold text-boost'>
          you stacked {numWithUnits(n.earnedSats, { abbreviate: false })} in rewards<small className='text-muted ms-1 fw-normal' suppressHydrationWarning>{time}</small>
        </div>
        {n.sources &&
          <div style={{ fontSize: '80%', color: 'var(--theme-grey)' }}>
            {n.sources.posts > 0 && <span>{numWithUnits(n.sources.posts, { abbreviate: false })} for top posts</span>}
            {n.sources.comments > 0 && <span>{n.sources.posts > 0 && ' \\ '}{numWithUnits(n.sources.comments, { abbreviate: false })} for top comments</span>}
            {n.sources.tipPosts > 0 && <span>{(n.sources.comments > 0 || n.sources.posts > 0) && ' \\ '}{numWithUnits(n.sources.tipPosts, { abbreviate: false })} for zapping top posts early</span>}
            {n.sources.tipComments > 0 && <span>{(n.sources.comments > 0 || n.sources.posts > 0 || n.sources.tipPosts > 0) && ' \\ '}{numWithUnits(n.sources.tipComments, { abbreviate: false })} for zapping top comments early</span>}
          </div>}
        <div style={{ lineHeight: '140%' }}>
          SN distributes the sats it earns back to its best stackers daily. These sats come from <Link href='/~jobs'>jobs</Link>, boosts, posting fees, and donations. You can see the daily rewards pool and make a donation <Link href='/rewards'>here</Link>.
        </div>
        <small className='text-muted ms-1 pb-1 fw-normal'>click for details</small>
      </div>
    </div>
  )
}

function RevenueNotification ({ n }) {
  return (
    <div className='d-flex ms-2 py-1'>
      <BountyIcon className='align-self-center fill-success mx-1' width={24} height={24} style={{ flex: '0 0 24px' }} />
      <div className='ms-2 pb-1'>
        <div className='fw-bold text-success'>
          you stacked {numWithUnits(n.earnedSats, { abbreviate: false })} in territory revenue<small className='text-muted ms-1 fw-normal' suppressHydrationWarning>{timeSince(new Date(n.sortTime))}</small>
        </div>
        <div style={{ lineHeight: '140%' }}>
          As the founder of territory <Link href={`/~${n.subName}`}>~{n.subName}</Link>, you receive 50% of the revenue it generates and the other 50% go to <Link href='/rewards'>rewards</Link>.
        </div>
      </div>
    </div>
  )
}

function SubStatus ({ n }) {
  const dueDate = nextBillingWithGrace(n.sub)
  return (
    <div className={`fw-bold text-${n.sub.status === 'ACTIVE' ? 'success' : 'danger'} ms-2`}>
      {n.sub.status === 'ACTIVE'
        ? 'your territory is active again'
        : (n.sub.status === 'GRACE'
            ? <>your territory payment for ~{n.sub.name} is due or your territory will be archived in <LongCountdown date={dueDate} /></>
            : <>your territory ~{n.sub.name} has been archived</>)}
      <small className='text-muted d-block pb-1 fw-normal'>click to visit territory and pay</small>
    </div>
  )
}

function Invitification ({ n }) {
  return (
    <>
      <small className='fw-bold text-secondary ms-2'>
        your invite has been redeemed by
        {numWithUnits(n.invite.invitees.length, {
          abbreviate: false,
          unitSingular: 'stacker',
          unitPlural: 'stackers'
        })}
      </small>
      <div className='ms-4 me-2 mt-1'>
        <Invite
          invite={n.invite} active={
          !n.invite.revoked &&
          !(n.invite.limit && n.invite.invitees.length >= n.invite.limit)
        }
        />
      </div>
    </>
  )
}

function NostrZap ({ n }) {
  const { nostr } = n.invoice
  const { npub, content, note } = nostrZapDetails(nostr)

  return (
    <>
      <div className='fw-bold text-nostr ms-2 py-1'>
        <NostrIcon width={24} height={24} className='fill-nostr me-1' />{numWithUnits(n.earnedSats)} zap from
        <Link className='mx-1 text-reset text-underline' target='_blank' href={`https://snort.social/p/${npub}`} rel='noreferrer'>
          {npub.slice(0, 10)}...
        </Link>
        on {note
          ? (
            <Link className='mx-1 text-reset text-underline' target='_blank' href={`https://snort.social/e/${note}`} rel='noreferrer'>
              {note.slice(0, 12)}...
            </Link>)
          : 'nostr'}
        <small className='text-muted ms-1 fw-normal' suppressHydrationWarning>{timeSince(new Date(n.sortTime))}</small>
        {content && <small className='d-block ms-4 ps-1 mt-1 mb-1 text-muted fw-normal'><Text>{content}</Text></small>}
      </div>
    </>
  )
}

function InvoicePaid ({ n }) {
  let payerSig
  if (n.invoice.lud18Data) {
    const { name, identifier, email, pubkey } = n.invoice.lud18Data
    const id = identifier || email || pubkey
    payerSig = '- '
    if (name) {
      payerSig += name
      if (id) payerSig += ' \\ '
    }

    if (id) payerSig += id
  }
  return (
    <div className='fw-bold text-info ms-2 py-1'>
      <Check className='fill-info me-1' />{numWithUnits(n.earnedSats, { abbreviate: false, unitSingular: 'sat was', unitPlural: 'sats were' })} deposited in your account
      <small className='text-muted ms-1 fw-normal' suppressHydrationWarning>{timeSince(new Date(n.sortTime))}</small>
      {n.invoice.comment &&
        <small className='d-block ms-4 ps-1 mt-1 mb-1 text-muted fw-normal'>
          <Text>{n.invoice.comment}</Text>
          {payerSig}
        </small>}
    </div>
  )
}

function Referral ({ n }) {
  return (
    <small className='fw-bold text-secondary ms-2'>
      someone joined via one of your referral links
      <small className='text-muted ms-1 fw-normal' suppressHydrationWarning>{timeSince(new Date(n.sortTime))}</small>
    </small>
  )
}

function Votification ({ n }) {
  let forwardedSats = 0
  let ForwardedUsers = null
  if (n.item.forwards?.length) {
    forwardedSats = Math.floor(n.earnedSats * n.item.forwards.map(fwd => fwd.pct).reduce((sum, cur) => sum + cur) / 100)
    ForwardedUsers = () => n.item.forwards.map((fwd, i) =>
      <span key={fwd.user.name}>
        <Link className='text-success' href={`/${fwd.user.name}`}>
          @{fwd.user.name}
        </Link>
        {i !== n.item.forwards.length - 1 && ' '}
      </span>)
  }
  return (
    <>
      <small className='fw-bold text-success d-inline-block ms-2 my-1' style={{ lineHeight: '1.25' }}>
        your {n.item.title ? 'post' : 'reply'} stacked {numWithUnits(n.earnedSats, { abbreviate: false })}
        {n.item.forwards?.length > 0 &&
          <>
            {' '}and forwarded {numWithUnits(forwardedSats, { abbreviate: false })} to{' '}
            <ForwardedUsers />
          </>}
      </small>
      <div>
        {n.item.title
          ? <Item item={n.item} />
          : (
            <div className='pb-2'>
              <RootProvider root={n.item.root}>
                <Comment item={n.item} noReply includeParent clickToContext />
              </RootProvider>
            </div>
            )}
      </div>
    </>
  )
}

function ForwardedVotification ({ n }) {
  return (
    <>
      <small className='fw-bold text-success d-inline-block ms-2 my-1' style={{ lineHeight: '1.25' }}>
        you were forwarded {numWithUnits(n.earnedSats, { abbreviate: false })} from
      </small>
      <div>
        {n.item.title
          ? <Item item={n.item} />
          : (
            <div className='pb-2'>
              <RootProvider root={n.item.root}>
                <Comment item={n.item} noReply includeParent clickToContext />
              </RootProvider>
            </div>
            )}
      </div>
    </>
  )
}

function Mention ({ n }) {
  return (
    <>
      <small className='fw-bold text-info ms-2'>
        you were mentioned in
      </small>
      <div>
        {n.item.title
          ? <Item item={n.item} />
          : (
            <div className='pb-2'>
              <RootProvider root={n.item.root}>
                <Comment item={n.item} noReply includeParent rootText={n.__typename === 'Reply' ? 'replying on:' : undefined} clickToContext />
              </RootProvider>
            </div>)}
      </div>
    </>
  )
}

function JobChanged ({ n }) {
  return (
    <>
      <small className={`fw-bold text-${n.item.status === 'ACTIVE' ? 'success' : 'boost'} ms-1`}>
        {n.item.status === 'ACTIVE'
          ? 'your job is active again'
          : (n.item.status === 'NOSATS'
              ? 'your job promotion ran out of sats'
              : 'your job has been stopped')}
      </small>
      <ItemJob item={n.item} />
    </>
  )
}

function Reply ({ n }) {
  return (
    <div className='py-2'>
      {n.item.title
        ? <Item item={n.item} />
        : (
          <div className='pb-2'>
            <RootProvider root={n.item.root}>
              <Comment item={n.item} noReply includeParent clickToContext rootText='replying on:' />
            </RootProvider>
          </div>
          )}
    </div>
  )
}

function FollowActivity ({ n }) {
  return (
    <>
      <small className='fw-bold text-info ms-2'>
        a stacker you subscribe to {n.item.parentId ? 'commented' : 'posted'}
      </small>
      {n.item.title
        ? <div className='ms-2'><Item item={n.item} /></div>
        : (
          <div className='pb-2'>
            <RootProvider root={n.item.root}>
              <Comment item={n.item} noReply includeParent clickToContext rootText='replying on:' />
            </RootProvider>
          </div>
          )}
    </>
  )
}

function TerritoryPost ({ n }) {
  return (
    <>
      <small className='fw-bold text-info ms-2'>
        new post in ~{n.item.sub.name}
      </small>
      <div>
        <Item item={n.item} />
      </div>
    </>
  )
}

export function NotificationAlert () {
  const [showAlert, setShowAlert] = useState(false)
  const [hasSubscription, setHasSubscription] = useState(false)
  const [error, setError] = useState(null)
  const [supported, setSupported] = useState(false)
  const sw = useServiceWorker()

  useEffect(() => {
    const isSupported = sw.support.serviceWorker && sw.support.pushManager && sw.support.notification
    if (isSupported) {
      const isDefaultPermission = sw.permission.notification === 'default'
      setShowAlert(isDefaultPermission && !window.localStorage.getItem('hideNotifyPrompt'))
      sw.registration?.pushManager.getSubscription().then(subscription => setHasSubscription(!!subscription))
      setSupported(true)
    }
  }, [sw])

  const close = () => {
    window.localStorage.setItem('hideNotifyPrompt', 'yep')
    setShowAlert(false)
  }

  return (
    error
      ? (
        <Alert variant='danger' dismissible onClose={() => setError(null)}>
          <span>{error.toString()}</span>
        </Alert>
        )
      : showAlert
        ? (
          <Alert variant='info' dismissible onClose={close}>
            <span className='align-middle'>Enable push notifications?</span>
            <button
              className={`${styles.alertBtn} mx-1`}
              onClick={async () => {
                await sw.requestNotificationPermission()
                  .then(close)
                  .catch(setError)
              }}
            >Yes
            </button>
            <button className={styles.alertBtn} onClick={close}>No</button>
          </Alert>
          )
        : (
          <Form className={`d-flex justify-content-end ${supported ? 'visible' : 'invisible'}`} initial={{ pushNotify: hasSubscription }}>
            <Checkbox
              name='pushNotify' label={<span className='text-muted'>push notifications</span>}
              groupClassName={`${styles.subFormGroup} mb-1 me-sm-3 me-0`}
              inline checked={hasSubscription} handleChange={async () => {
                await sw.togglePushSubscription().catch(setError)
              }}
            />
          </Form>
          )
  )
}

const nid = n => n.__typename + n.id + n.sortTime

export default function Notifications ({ ssrData }) {
  const { data, fetchMore } = useQuery(NOTIFICATIONS)
  const router = useRouter()
  const dat = useData(data, ssrData)

  const { notifications: { notifications, lastChecked, cursor } } = useMemo(() => {
    return dat || { notifications: {} }
  }, [dat])

  useEffect(() => {
    if (lastChecked && !router?.query?.checkedAt) {
      router.replace({
        pathname: router.pathname,
        query: {
          ...router.query,
          nodata: true, // make sure nodata is set so we don't fetch on back/forward
          checkedAt: lastChecked
        }
      }, router.asPath, { ...router.options, shallow: true })
    }
  }, [router, lastChecked])

  if (!dat) return <CommentsFlatSkeleton />

  return (
    <>
      {notifications.map(n =>
        <Notification
          n={n} key={nid(n)}
          fresh={new Date(n.sortTime) > new Date(router?.query?.checkedAt)}
        />)}
      <MoreFooter cursor={cursor} count={notifications?.length} fetchMore={fetchMore} Skeleton={CommentsFlatSkeleton} noMoreText='NO MORE' />
    </>
  )
}

function CommentsFlatSkeleton () {
  const comments = new Array(21).fill(null)

  return (
    <div>
      {comments.map((_, i) => (
        <CommentSkeleton key={i} skeletonChildren={0} />
      ))}
    </div>
  )
}