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

import type { MouseEvent, ReactNode } from 'react'
import { useRequest, useToggle } from 'ahooks'
import {
  AlertTriangleIcon,
  BanIcon,
  CalendarIcon,
  CircleXIcon,
  ClockAlertIcon,
  ClockCheckIcon,
  ClockIcon,
  EllipsisIcon,
  PlusIcon,
  RocketIcon,
  UserIcon
} from 'lucide-react'
import { toast } from 'sonner'
import { DateTime } from 'luxon'
import { Fragment, Suspense } from 'react'
import { Await } from 'react-router'

import { request as req } from '@repo/util/request'
import { Button } from '@repo/ui/components/ui/button'
import {
  Empty,
  EmptyContent,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle
} from '@repo/ui/components/ui/empty'
import { Skeleton } from '@repo/ui/components/skeleton'
import { Card, CardContent } from '@repo/ui/components/ui/card'
import {
  Item,
  ItemActions,
  ItemContent,
  ItemDescription,
  ItemGroup,
  ItemMedia,
  ItemSeparator,
  ItemTitle
} from '@repo/ui/components/ui/item'
import { Link } from 'react-router'
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar'
import { initials } from '@repo/ui/lib/utils'
import { Abbr } from '@repo/ui/components/abbr'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger
} from '@repo/ui/components/ui/dropdown-menu'
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger
} from '@repo/ui/components/ui/alert-dialog'
import { Spinner } from '@repo/ui/components/ui/spinner'
import { useParams } from 'react-router'
import { useRevalidator } from 'react-router'
import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger
} from '@repo/ui/components/ui/tabs'
import { useSearchParams } from 'react-router'

export function meta({}: Route.MetaArgs) {
  return [{ title: 'Matrículas agendadas' }]
}

export async function loader({ context, request, params }: Route.LoaderArgs) {
  const scheduled = req({
    url: `/orgs/${params.orgid}/enrollments/scheduled`,
    context,
    request
  }).then((r) => r.json())

  return {
    scheduled
  }
}

export default function Route({
  loaderData: { scheduled }
}: Route.ComponentProps) {
  const [searchParams, setSearchParams] = useSearchParams()

  return (
    <Suspense fallback={<Skeleton />}>
      <div className="space-y-0.5 mb-8">
        <h1 className="text-2xl font-bold tracking-tight">
          Matrículas agendadas
        </h1>
        <p className="text-muted-foreground">
          Acompanhe todas as matrículas agendadas, cancele quando quiser ou
          matricule imediatamente.
        </p>
      </div>

      <Await resolve={scheduled}>
        {({ items }) => {
          if (items.length === 0) {
            return (
              <Empty className="border border-dashed">
                <EmptyHeader>
                  <EmptyMedia variant="icon">
                    <BanIcon />
                  </EmptyMedia>
                  <EmptyTitle>Nenhum agendamento ainda</EmptyTitle>
                  <EmptyDescription>
                    Agende a matrícula dos seus colaboradores de forma rápida e
                    organizada.
                  </EmptyDescription>
                </EmptyHeader>
                <EmptyContent>
                  <Button asChild>
                    <Link to="../enrollments/add">
                      <PlusIcon /> Agendar
                    </Link>
                  </Button>
                </EmptyContent>
              </Empty>
            )
          }

          const scheduled = grouping(filtering(items, undefined))
          const executed = grouping(filtering(items, 'EXECUTED'))
          const failed = grouping(filtering(items, 'FAILED'))

          return (
            <div className="space-y-5 lg:max-w-4xl mx-auto">
              <Tabs
                defaultValue={String(
                  searchParams.has('tab') ? searchParams.get('tab') : 'pending'
                )}
                className="space-y-5"
                onValueChange={(value) => {
                  setSearchParams((searchParams) => {
                    searchParams.set('tab', value)
                    return searchParams
                  })
                }}
              >
                <div className="flex justify-between">
                  <TabsList className="*:cursor-pointer">
                    <TabsTrigger value="pending">Aguardando</TabsTrigger>
                    <TabsTrigger value="executed">Executada</TabsTrigger>
                    <TabsTrigger value="failed">Falhou</TabsTrigger>
                  </TabsList>

                  <Button asChild>
                    <Link to="../enrollments/add">
                      <PlusIcon />{' '}
                      <span className="hidden xl:block">Agendar</span>
                    </Link>
                  </Button>
                </div>

                <TabsContent value="pending" className="space-y-5">
                  <Timeline events={scheduled}>
                    {({ items }) => (
                      <Card className="col-span-4">
                        <CardContent>
                          <Scheduled items={items} />
                        </CardContent>
                      </Card>
                    )}
                  </Timeline>
                </TabsContent>

                <TabsContent value="executed" className="space-y-5">
                  <Timeline events={executed}>
                    {({ items }) => (
                      <Card className="col-span-4">
                        <CardContent>
                          <Executed items={items} />
                        </CardContent>
                      </Card>
                    )}
                  </Timeline>
                </TabsContent>

                <TabsContent value="failed" className="space-y-5">
                  <Timeline events={failed}>
                    {({ items }) => (
                      <Card className="col-span-4">
                        <CardContent>
                          <Failed items={items} />
                        </CardContent>
                      </Card>
                    )}
                  </Timeline>
                </TabsContent>
              </Tabs>
            </div>
          )
        }}
      </Await>
    </Suspense>
  )
}

function Timeline({
  events = [],
  children
}: {
  events: any[]
  children: (props: any) => ReactNode
}) {
  if (events.length === 0) {
    return (
      <Empty className="border border-dashed">
        <EmptyHeader>
          <EmptyMedia variant="icon">
            <ClockIcon />
          </EmptyMedia>
          <EmptyTitle>Nenhum agendamento encontrado</EmptyTitle>
          <EmptyDescription>
            Ainda não há agendamentos. Quando houver, eles aparecerão aqui.
          </EmptyDescription>
        </EmptyHeader>
      </Empty>
    )
  }

  return (
    <>
      {events.map(([run_at, items], index) => (
        <div className="grid grid-cols-1 lg:grid-cols-5 gap-2.5" key={index}>
          <div>
            {DateTime.fromISO(run_at)
              .setLocale('pt-BR')
              .toFormat('cccc, dd LLL yyyy')}
          </div>

          {children({ items })}
        </div>
      ))}
    </>
  )
}

function Scheduled({ items = [] }) {
  return (
    <ItemGroup>
      {items.map(({ sk, user, course, created_by, scheduled_at }, index) => (
        <Fragment key={index}>
          <Item className="max-lg:px-0 max-lg:first:pt-0 max-lg:last:pb-0">
            <ItemMedia className="hidden lg:block">
              <Avatar className="size-10 ">
                <AvatarFallback className="border">
                  {initials(user.name)}
                </AvatarFallback>
              </Avatar>
            </ItemMedia>

            <ItemContent>
              <ItemTitle>{course.name}</ItemTitle>
              <ItemDescription className="flex flex-col">
                <Abbr>{user.name}</Abbr>
                <Abbr>{user.email}</Abbr>
              </ItemDescription>

              <div className="mt-1">
                <ul className="lg:flex gap-2.5 text-muted-foreground text-sm *:flex *:gap-1 *:items-center">
                  <li>
                    <CalendarIcon className="size-3.5" />
                    <span>{datetime.format(new Date(scheduled_at))}</span>
                  </li>
                  <li>
                    <UserIcon className="size-3.5" />
                    <span>{created_by.name}</span>
                  </li>
                </ul>
              </div>
            </ItemContent>

            <ItemActions className="self-start">
              <ActionMenu sk={sk} />
            </ItemActions>
          </Item>

          {index !== items.length - 1 && <ItemSeparator />}
        </Fragment>
      ))}
    </ItemGroup>
  )
}

function Executed({ items = [] }) {
  return (
    <ItemGroup>
      {items.length === 0 ? (
        <Empty className="border border-dashed">
          <EmptyHeader>
            <EmptyMedia variant="icon">
              <BanIcon />
            </EmptyMedia>
            <EmptyTitle>Nenhum agendamento ainda</EmptyTitle>
            <EmptyDescription>
              Agende a matrícula dos seus colaboradores de forma rápida e
              organizada.
            </EmptyDescription>
          </EmptyHeader>
          <EmptyContent>
            <Button asChild>
              <Link to="../enrollments/add">
                <PlusIcon /> Agendar
              </Link>
            </Button>
          </EmptyContent>
        </Empty>
      ) : null}

      {items.map(({ course, user, created_at }, index) => (
        <Fragment key={index}>
          <Item className="max-lg:px-0 max-lg:first:pt-0 max-lg:last:pb-0">
            <ItemMedia className="hidden lg:block">
              <Avatar className="size-10 ">
                <AvatarFallback className="border">
                  {initials(user.name)}
                </AvatarFallback>
              </Avatar>
            </ItemMedia>

            <ItemContent>
              <ItemTitle>{course.name}</ItemTitle>
              <ItemDescription className="flex flex-col">
                <Abbr>{user.name}</Abbr>
                <Abbr>{user.email}</Abbr>
              </ItemDescription>

              <div className="mt-1">
                <ul className="lg:flex gap-2.5 text-muted-foreground text-sm *:flex *:gap-1 *:items-center">
                  <li>
                    <ClockCheckIcon className="size-3.5" />
                    <span>{datetime.format(new Date(created_at))}</span>
                  </li>
                </ul>
              </div>
            </ItemContent>
          </Item>

          {index !== items.length - 1 && <ItemSeparator />}
        </Fragment>
      ))}
    </ItemGroup>
  )
}

function Failed({ items = [] }) {
  return (
    <ItemGroup>
      {items.map(({ snapshot: { course, user }, cause, created_at }, index) => (
        <Fragment key={index}>
          <Item className="max-lg:px-0 max-lg:first:pt-0 max-lg:last:pb-0">
            <ItemMedia className="hidden lg:block">
              <Avatar className="size-10 ">
                <AvatarFallback className="border">
                  {initials(user.name)}
                </AvatarFallback>
              </Avatar>
            </ItemMedia>

            <ItemContent>
              <ItemTitle>{course.name}</ItemTitle>
              <ItemDescription className="flex flex-col">
                <Abbr>{user.name}</Abbr>
                <Abbr>{user.email}</Abbr>
              </ItemDescription>

              <div className="mt-1">
                <ul className="lg:flex gap-2.5 text-muted-foreground text-sm *:flex *:gap-1 *:items-center">
                  <li>
                    <ClockAlertIcon className="size-3.5" />
                    <span>{datetime.format(new Date(created_at))}</span>
                  </li>

                  {cause?.type === 'DeduplicationConflictError' ? (
                    <li className="text-red-400">
                      <AlertTriangleIcon className="size-3.5" />
                      <span>Protegido contra duplicação</span>
                    </li>
                  ) : null}
                </ul>
              </div>
            </ItemContent>
          </Item>

          {index !== items.length - 1 && <ItemSeparator />}
        </Fragment>
      ))}
    </ItemGroup>
  )
}

function ActionMenu({ sk }: { sk: string }) {
  const { revalidate } = useRevalidator()

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

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="ghost" size="icon-sm" className="cursor-pointer">
          <EllipsisIcon />
        </Button>
      </DropdownMenuTrigger>

      <DropdownMenuContent align="end" className="*:cursor-pointer w-42">
        <Proceedtem sk={sk} onSuccess={onSuccess} />
        <DropdownMenuSeparator />
        <CancelItem sk={sk} onSuccess={onSuccess} />
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

function Proceedtem({ sk, onSuccess }: { sk: string; onSuccess?: () => void }) {
  const { runAsync, loading } = useRequest(
    async () => {
      await new Promise((r) => setTimeout(r, 1000))
    },
    { manual: true }
  )

  const proceed = async (e: MouseEvent) => {
    e.preventDefault()
    await runAsync()
  }

  return (
    <DropdownMenuItem onClick={proceed}>
      {loading ? <Spinner /> : <RocketIcon />} Matricular agora
    </DropdownMenuItem>
  )
}

function CancelItem({ sk, onSuccess }: { sk: string; onSuccess?: () => void }) {
  const { orgid } = useParams()
  const [open, { set: setOpen }] = useToggle(false)
  const { runAsync, loading } = useRequest(
    async () => {
      const [scheduled_for, lock_hash] = sk.split('#')
      return await fetch(`/~/api/orgs/${orgid}/enrollments/scheduled`, {
        method: 'DELETE',
        headers: new Headers({ 'Content-Type': 'application/json' }),
        body: JSON.stringify({ scheduled_for, lock_hash })
      })
    },
    { manual: true }
  )

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

    const r = await runAsync()
    if (r.ok) {
      toast.info('O agendamento foi cancelada.')
      onSuccess?.()
    }

    setOpen(false)
  }

  return (
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogTrigger asChild>
        <DropdownMenuItem
          variant="destructive"
          onSelect={(e) => e.preventDefault()}
        >
          <CircleXIcon /> Cancelar
        </DropdownMenuItem>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>Tem certeza absoluta?</AlertDialogTitle>
          <AlertDialogDescription>
            Esta ação não pode ser desfeita. Isso{' '}
            <span className="font-bold">
              cancela permanentemente o agendamento
            </span>{' '}
            desta matrícula.
          </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>
  )
}

function filtering(items, status) {
  return items.filter(({ sk }: { sk: string }) => {
    const [, , s] = sk.split('#')
    return s == status
  })
}

function grouping(items) {
  const newItems = Object.entries(
    items.reduce((acc, item) => {
      const [run_at] = item.sk.split('#')

      if (!acc[run_at]) {
        acc[run_at] = []
      }

      acc[run_at].push(item)

      return acc
    }, [])
  )
  return newItems.sort((x, y) => x[0].localeCompare(y[0]))
}

const datetime = new Intl.DateTimeFormat('pt-BR', {
  day: '2-digit',
  month: '2-digit',
  year: 'numeric',
  hour: '2-digit',
  minute: '2-digit'
})
