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

import Fuse from 'fuse.js'
import { AwardIcon, BanIcon, LaptopIcon } from 'lucide-react'
import { Suspense, useMemo } from 'react'
import { Await, useSearchParams } from 'react-router'

import { SearchForm } from '@repo/ui/components/search-form'
import { Skeleton } from '@repo/ui/components/skeleton'
import {
  Card,
  CardFooter,
  CardHeader,
  CardTitle
} from '@repo/ui/components/ui/card'
import {
  Empty,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle
} from '@repo/ui/components/ui/empty'
import { Kbd } from '@repo/ui/components/ui/kbd'
import { cn } from '@repo/ui/lib/utils'
import { createSearch } from '@repo/util/meili'
import { request as req } from '@repo/util/request'

import placeholder from '@/assets/placeholder.webp'

type Cert = {
  exp_interval: number
}

type Course = {
  id: string
  name: string
  access_period: string
  cert: Cert
  metadata__unit_price?: number
}

type CustomPricing = {
  sk: string
  unit_price: number
}

export function meta({}: Route.MetaArgs) {
  return [{ title: 'Catálogo de cursos' }]
}

export async function loader({ context, request, params }: Route.LoaderArgs) {
  const courses = createSearch({
    index: 'saladeaula_courses',
    sort: ['created_at:desc'],
    filter: 'unlisted NOT EXISTS',
    hitsPerPage: 100,
    env: context.cloudflare.env
  })

  const customPricing = req({
    url: `/orgs/${params.orgid}/custom-pricing`,
    context,
    request
  }).then((r) => r.json() as Promise<{ items: CustomPricing[] }>)

  return {
    data: Promise.all([courses, customPricing])
  }
}

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

  return (
    <Suspense fallback={<Skeleton />}>
      <div className="space-y-0.5 mb-8">
        <h1 className="text-2xl font-bold tracking-tight">
          Catálogo de cursos
        </h1>
        <p className="text-muted-foreground">
          Explore o catálogo de cursos disponíveis para sua empresa.
        </p>
      </div>

      <Await resolve={data}>
        {([{ hits }, { items }]) => {
          return (
            <>
              <div className="flex flex-col lg:flex-row justify-between gap-2.5 mb-2.5">
                <div className="2xl:w-92">
                  <SearchForm
                    placeholder={
                      <>
                        Digite <Kbd className="border font-mono">/</Kbd> para
                        pesquisar
                      </>
                    }
                    defaultValue={search}
                    onChange={(search) => {
                      setSearchParams({ s: String(search) })
                    }}
                  />
                </div>
              </div>

              <List
                search={search}
                hits={hits as Course[]}
                customPricing={items}
              />
            </>
          )
        }}
      </Await>
    </Suspense>
  )
}

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

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

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

  const customPricingMap = new Map(
    customPricing.map((x) => {
      const [, courseId] = x.sk.split('#')
      return [courseId, x.unit_price]
    })
  )

  if (hits_.length === 0) {
    return (
      <Empty className="border border-dashed">
        <EmptyHeader>
          <EmptyMedia variant="icon">
            <BanIcon />
          </EmptyMedia>
          <EmptyTitle>Nada encontrado</EmptyTitle>
          <EmptyDescription>
            Nenhum resultado para <mark>{search}</mark>.
          </EmptyDescription>
        </EmptyHeader>
      </Empty>
    )
  }

  return (
    <div className="grid lg:grid-cols-3 xl:grid-cols-4 gap-5">
      {hits_
        .filter(({ metadata__unit_price = 0 }) => metadata__unit_price > 0)
        .map((props: Course, idx) => {
          return (
            <Course
              key={idx}
              custom_pricing={customPricingMap.get(props.id)}
              {...props}
            />
          )
        })}
    </div>
  )
}

function Course({
  name,
  access_period,
  cert,
  metadata__unit_price,
  custom_pricing
}: Course & { custom_pricing?: number }) {
  return (
    <Card className="overflow-hidden relative h-96">
      <CardHeader className="z-1 relative">
        <CardTitle className="text-xl/6">{name}</CardTitle>
      </CardHeader>

      <CardFooter className="text-gray-300 text-sm absolute z-1 bottom-6 w-full flex gap-1.5">
        <ul className="flex gap-2.5">
          <li className="flex gap-0.5 items-center">
            <LaptopIcon className="size-4" />
            <span>{access_period}d</span>
          </li>

          {cert?.exp_interval && (
            <li className="flex gap-0.5 items-center">
              <AwardIcon className="size-4" />
              <span>{cert.exp_interval}d</span>
            </li>
          )}

          {metadata__unit_price && (
            <>
              <li className="flex gap-1.5">
                <span
                  className={cn({
                    'line-through text-muted-foreground': custom_pricing,
                    'font-bold': !custom_pricing
                  })}
                >
                  {currency.format(metadata__unit_price)}
                </span>

                {custom_pricing && (
                  <span className="font-bold">
                    {currency.format(custom_pricing)}
                  </span>
                )}
              </li>
            </>
          )}
        </ul>
      </CardFooter>

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

const currency = new Intl.NumberFormat('pt-BR', {
  style: 'currency',
  currency: 'BRL'
})
