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

import Fuse from 'fuse.js'
import {
  BanIcon,
  CirclePlusIcon,
  EllipsisIcon,
  FileBadgeIcon,
  PresentationIcon,
  HelpCircleIcon
} from 'lucide-react'
import { useRequest } from 'ahooks'
import { MeiliSearchFilterBuilder } from 'meilisearch-helper'
import { Suspense, useMemo, type ComponentProps } from 'react'
import { Await, NavLink, useSearchParams } from 'react-router'

import type { User } from '@repo/auth/auth'
import { userContext } from '@repo/auth/context'
import { FacetedFilter } from '@repo/ui/components/faceted-filter'
import { SearchForm } from '@repo/ui/components/search-form'
import { Skeleton } from '@repo/ui/components/skeleton'
import { cn } from '@repo/ui/lib/utils'
import { Badge } from '@repo/ui/components/ui/badge'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger
} from '@repo/ui/components/ui/dropdown-menu'
import {
  Empty,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle
} from '@repo/ui/components/ui/empty'
import {
  Card,
  CardFooter,
  CardHeader,
  CardTitle,
  CardAction
} from '@repo/ui/components/ui/card'
import { Kbd } from '@repo/ui/components/ui/kbd'
import { Progress } from '@repo/ui/components/ui/progress'
import { createSearch } from '@repo/util/meili'
import { Button } from '@repo/ui/components/ui/button'
import {
  statuses,
  labels,
  type Enrollment
} from '@repo/ui/routes/enrollments/data'

import placeholder from '@/assets/placeholder.webp'
import { Container } from '@/components/container'
import { Spinner } from '@repo/ui/components/ui/spinner'

export function meta({}: Route.MetaArgs) {
  return [{ title: 'Meus cursos' }]
}

export async function loader({ request, context }: Route.ActionArgs) {
  const user = context.get(userContext) as User
  const { searchParams } = new URL(request.url)
  const status = searchParams.getAll('status') || []
  let builder = new MeiliSearchFilterBuilder().where('user.id', '=', user.sub)

  if (status.length) {
    builder = builder.where('status', 'in', status)
  }

  const enrollments = createSearch({
    index: 'betaeducacao-prod-enrollments',
    filter: builder.build(),
    sort: ['created_at:desc'],
    hitsPerPage: 100,
    env: context.cloudflare.env
  })

  return {
    data: enrollments
  }
}

export default function Component({
  loaderData: { data }
}: Route.ComponentProps) {
  const [searchParams, setSearchParams] = useSearchParams()
  const search = searchParams.get('s') as string

  return (
    <Container className="space-y-4">
      <Suspense fallback={<Skeleton />}>
        <div className="space-y-0.5 mb-8">
          <h1 className="text-2xl font-bold tracking-tight">Meus cursos</h1>
          <p className="text-muted-foreground">
            Aqui você encontra todos os cursos em que está matriculado,
            organizados em um só lugar. Acompanhe seu progresso e continue de
            onde parou.
          </p>
        </div>

        <div className="flex gap-2.5">
          <div className="w-full xl:w-1/3">
            <SearchForm
              defaultValue={search || ''}
              placeholder={
                <>
                  Digite <Kbd>/</Kbd> para pesquisar
                </>
              }
              onChange={(value) =>
                setSearchParams((searchParams) => {
                  searchParams.set('s', String(value))
                  return searchParams
                })
              }
            />
          </div>
          <FacetedFilter
            icon={CirclePlusIcon}
            value={searchParams.getAll('status')}
            onChange={(statuses) => {
              setSearchParams((searchParams) => {
                searchParams.delete('status')

                if (statuses.length) {
                  statuses.forEach((s) =>
                    searchParams.has('status', s)
                      ? null
                      : searchParams.append('status', s)
                  )
                }

                return searchParams
              })
            }}
            title="Status"
            options={Object.entries(statuses).map(([key, value]) => ({
              value: key,
              ...value
            }))}
          />
        </div>

        <Await resolve={data}>
          {({ hits = [] }) => (
            <List search={search} hits={hits as Enrollment[]} />
          )}
        </Await>
      </Suspense>
    </Container>
  )
}

function List({ search, hits = [] }: { search: string; hits: Enrollment[] }) {
  const fuse = useMemo(() => {
    return new Fuse(hits, {
      keys: ['course.name'],
      threshold: 0.3,
      includeMatches: true
    })
  }, [hits])

  const filtered = useMemo(() => {
    if (!search) {
      return hits
    }

    return fuse.search(search).map(({ item }) => item)
  }, [search, fuse, hits])

  if (filtered.length === 0) {
    return (
      <Empty className="border border-dashed">
        <EmptyHeader>
          <EmptyMedia variant="icon">
            <BanIcon />
          </EmptyMedia>
          {search ? (
            <>
              <EmptyTitle>Nada encontrado</EmptyTitle>
              <EmptyDescription>
                Nenhum resultado para <mark>{search}</mark>.
              </EmptyDescription>
            </>
          ) : (
            <>
              <EmptyTitle>Nenhum curso ainda</EmptyTitle>
              <EmptyDescription>
                Comece escolhendo um curso. Assim que você se matricular, ele
                aparecerá aqui.
              </EmptyDescription>
            </>
          )}
        </EmptyHeader>
      </Empty>
    )
  }

  return (
    <div className="grid lg:grid-cols-4 gap-5">
      {filtered.map((props: Enrollment, idx) => {
        return <Enrollment key={idx} {...props} />
      })}
    </div>
  )
}

function Enrollment(enrollment: Enrollment) {
  const { id, course, progress, status } = enrollment
  const disabled = ['CANCELED', 'FAILED'].includes(status)

  return (
    <NavLink
      to={course?.scormset ? `/player/${id}` : '/konviva'}
      onClick={(e) => {
        if (disabled) {
          e.preventDefault()
        }

        if ((e.target as HTMLElement).closest('[role="menu"]')) {
          e.preventDefault()
        }
      }}
    >
      <Card
        aria-disabled={disabled}
        className="overflow-hidden relative h-96 hover:scale-105
                   lg:has-data-[state=open]:scale-105 transition
                   aria-disabled:border-dashed"
      >
        <CardHeader className="z-1 relative">
          <CardTitle className="text-xl/6">{course.name}</CardTitle>
          <CardAction>
            <ActionMenu disabled={disabled} {...enrollment} />
          </CardAction>
          <Status status={status} />
        </CardHeader>

        <CardFooter className="absolute z-1 bottom-6 w-full flex gap-1.5">
          <Progress value={progress} />
          <span className="text-xs">{progress}%</span>
        </CardFooter>

        <img
          src={placeholder}
          alt={course.name}
          className="absolute bottom-0 opacity-75"
        />
      </Card>
    </NavLink>
  )
}

function ActionMenu({
  id,
  course,
  cert,
  disabled
}: Enrollment & { disabled?: boolean }) {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button
          variant="ghost"
          size="icon-sm"
          className="cursor-pointer data-[state=open]:bg-muted "
          disabled={disabled}
        >
          <EllipsisIcon />
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end" className="*:cursor-pointer">
        <ContinueItem id={id} course={course} />
        <DownloadItem id={id} disabled={!cert} />
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

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

function DownloadItem({ id, onSuccess, ...props }: ItemProps) {
  const { runAsync, loading } = useRequest(
    async () => {
      return await fetch(`/api/enrollments/${id}/download`)
    },
    {
      manual: true
    }
  )

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

    try {
      const r = await runAsync()
      const { presigned_url } = (await r.json()) as {
        presigned_url: string
      }

      window.open(presigned_url, '_blank')
      onSuccess?.()
    } catch {}
  }

  return (
    <DropdownMenuItem onSelect={download} {...props}>
      {loading ? <Spinner /> : <FileBadgeIcon />} Baixar certificado
    </DropdownMenuItem>
  )
}

function ContinueItem({ id, course }: ItemProps & { course: any }) {
  return (
    <DropdownMenuItem asChild>
      <NavLink to={course?.scormset ? `/player/${id}` : '/konviva'}>
        <PresentationIcon /> Continuar curso
      </NavLink>
    </DropdownMenuItem>
  )
}

function Status({ status: s }: { status: string }) {
  const status = labels[s] ?? s
  const { icon: Icon, color } = statuses?.[s] ?? { icon: HelpCircleIcon }

  return (
    <Badge variant="outline" className={cn(color, 'bg-card px-1.5')}>
      <Icon className={cn('stroke-2', color)} />
      {status}
    </Badge>
  )
}
