import type { Route } from './+types/index'

import {
  Suspense,
  type ComponentProps,
  type MouseEvent,
  createContext,
  use
} from 'react'
import { Await, useRevalidator } from 'react-router'
import { EllipsisIcon, CircleXIcon, SendIcon } from 'lucide-react'
import { useRequest, useToggle } from 'ahooks'
import { toast } from 'sonner'

import { Spinner } from '@repo/ui/components/ui/spinner'
import { userContext } from '@repo/auth/context'
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle
} from '@repo/ui/components/ui/card'
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger
} from '@repo/ui/components/ui/alert-dialog'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger
} from '@repo/ui/components/ui/dropdown-menu'
import { request as req } from '@repo/util/request'
import { Skeleton } from '@repo/ui/components/skeleton'
import { Button } from '@repo/ui/components/ui/button'
import { useOutletContext } from 'react-router'
import type { User } from '@repo/ui/routes/users/data'
import {
  Item,
  ItemActions,
  ItemContent,
  ItemDescription,
  ItemTitle
} from '@repo/ui/components/ui/item'
import { Badge } from '@repo/ui/components/ui/badge'

import { type Email } from './data'
import { Add } from './add'
import { Primary } from './primary'

const ActionMenuContext = createContext<Email | null>(null)

export async function loader({ request, context }: Route.LoaderArgs) {
  const user = context.get(userContext)

  const data = req({
    url: `/users/${user.sub}/emails`,
    request,
    context
  }).then((r) => r.json() as Promise<{ items: Email[] }>)

  return { data }
}

export default function Route({ loaderData: { data } }: Route.ComponentProps) {
  return (
    <div className="space-y-2.5">
      <Suspense fallback={<Skeleton />}>
        <Await resolve={data}>
          {({ items = [] }) => (
            <>
              <Card>
                <CardHeader>
                  <CardTitle className="text-lg">Emails</CardTitle>
                  <CardDescription>
                    Você pode associar vários emails a sua conta. É possível
                    usar qualquer email para recuperar a senha, mas apenas o
                    email principal receberá as mensagens.
                  </CardDescription>
                </CardHeader>
                <CardContent className="flex flex-col gap-2.5">
                  {items.map(({ sk, email_primary, email_verified }) => {
                    const [, email] = sk.split('#') as [string, string]

                    return (
                      <Item key={email} variant="outline">
                        <ItemContent>
                          <ItemTitle>{email}</ItemTitle>
                          <ItemDescription className="flex gap-1">
                            {email_primary && (
                              <Badge
                                variant="outline"
                                className="border-blue-500 text-blue-400 lowercase text-xs"
                              >
                                Principal
                              </Badge>
                            )}
                            {email_verified ? (
                              <Badge
                                variant="outline"
                                className="border-green-600 text-green-500 lowercase text-xs"
                              >
                                Verificado
                              </Badge>
                            ) : (
                              <Badge
                                variant="outline"
                                className="border-yellow-600 text-yellow-500 lowercase text-xs"
                              >
                                Não verificado
                              </Badge>
                            )}
                          </ItemDescription>
                        </ItemContent>
                        <ItemActions>
                          <ActionMenuContext
                            value={{ sk, email, email_primary, email_verified }}
                          >
                            <ActionMenu />
                          </ActionMenuContext>
                        </ItemActions>
                      </Item>
                    )
                  })}
                </CardContent>

                <CardContent>
                  <Add />
                </CardContent>
              </Card>

              <Primary items={items} />
            </>
          )}
        </Await>
      </Suspense>
    </div>
  )
}

function ActionMenu() {
  const { revalidate } = useRevalidator()

  const onSuccess = () => {
    revalidate()
  }

  return (
    <DropdownMenu modal={false}>
      <DropdownMenuTrigger asChild>
        <Button variant="ghost" size="icon-sm" className="cursor-pointer">
          <EllipsisIcon />
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end" className="*:cursor-pointer">
        <ResendItem onSuccess={onSuccess} />
        <RemoveItem onSuccess={onSuccess} />
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

type ItemProps = ComponentProps<typeof DropdownMenuItem> & {
  onSuccess?: () => void
}

function ResendItem({ onSuccess }: ItemProps) {
  const { user } = useOutletContext() as { user: User }
  const { email, email_verified } = use(ActionMenuContext) as Email
  const { runAsync, loading } = useRequest(
    async () => {
      const r = await fetch(
        `/api/users/${user.id}/emails/${email}/request-verification`,
        {
          method: 'POST',
          headers: new Headers({ 'Content-Type': 'application/json' })
        }
      )

      if (!r.ok) {
        throw await r.json()
      }
    },
    {
      manual: true
    }
  )

  const resend = async (e: Event) => {
    e.preventDefault()

    try {
      await runAsync()

      toast.success(
        `Enviamos um email de verificação para ${email}. Por favor, siga as instruções contidas nele.`
      )
      onSuccess?.()
    } catch {}
  }

  return (
    <DropdownMenuItem onSelect={resend} disabled={email_verified}>
      {loading ? <Spinner /> : <SendIcon />} Reenviar email de verificação
    </DropdownMenuItem>
  )
}

function RemoveItem({ onSuccess, ...props }: ItemProps) {
  const { user } = useOutletContext() as { user: User }
  const { email, email_primary } = use(ActionMenuContext) as Email
  const [open, { set: setOpen }] = useToggle(false)
  const { runAsync, loading } = useRequest(
    async () => {
      const r = await fetch(`/api/users/${user.id}/emails/${email}`, {
        method: 'DELETE',
        headers: new Headers({ 'Content-Type': 'application/json' })
      })

      if (!r.ok) {
        throw await r.json()
      }
    },
    {
      manual: true
    }
  )

  const cancel = async (e: MouseEvent<HTMLButtonElement>) => {
    e.preventDefault()
    try {
      await runAsync()

      toast.info(`O endereço de email ${email} foi removido.`)
      onSuccess?.()
    } catch (err) {
      // @ts-ignore
      if (err?.type === 'EmailConflictError') {
        toast.error(`O endereço de email ${email} não pode ser removido.`)
      }
    }

    setOpen(false)
  }

  if (email_primary) {
    return null
  }

  return (
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogTrigger asChild>
        <DropdownMenuItem
          variant="destructive"
          onSelect={(e) => e.preventDefault()}
          {...props}
        >
          <CircleXIcon /> Remover
        </DropdownMenuItem>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>Tem certeza absoluta?</AlertDialogTitle>
          <AlertDialogDescription>
            Esta ação não pode ser desfeita. Isso{' '}
            <span className="font-bold">remove permanentemente</span> o seu
            endereço de email.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter className="*:cursor-pointer">
          <AlertDialogAction asChild>
            <Button onClick={cancel} disabled={loading} variant="destructive">
              {loading ? <Spinner /> : null} Continuar
            </Button>
          </AlertDialogAction>
          <AlertDialogCancel>Cancelar</AlertDialogCancel>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )
}
