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

import { Fragment, use, useEffect, type ReactNode } from 'react'
import { useRequest, useToggle } from 'ahooks'
import { ErrorMessage } from '@hookform/error-message'
import {
  CopyIcon,
  CopyPlusIcon,
  Trash2Icon,
  PlusIcon,
  EllipsisIcon
} from 'lucide-react'
import { redirect, Link, useParams, useFetcher } from 'react-router'
import { Controller, useFieldArray, useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { pick } from 'ramda'

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandItem,
  CommandList
} from '@repo/ui/components/ui/command'
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator
} from '@repo/ui/components/ui/breadcrumb'
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle
} from '@repo/ui/components/ui/card'
import { DateTime } from '@repo/ui/components/datetime'
import { Spinner } from '@repo/ui/components/ui/spinner'
import { Input } from '@repo/ui/components/ui/input'
import { Button } from '@repo/ui/components/ui/button'
import { Separator } from '@repo/ui/components/ui/separator'
import {
  Popover,
  PopoverContent,
  PopoverTrigger
} from '@repo/ui/components/ui/popover'
import { Label } from '@repo/ui/components/ui/label'
import { createSearch } from '@repo/util/meili'
import { HttpMethod, request as req } from '@repo/util/request'
import { useIsMobile } from '@repo/ui/hooks/use-mobile'
import { cloudflareContext } from '@repo/auth/context'

import {
  MAX_ITEMS,
  formSchema,
  type Schema,
  type Course,
  type User,
  type Enrolled
} from './data'
import { ScheduledForInput } from './scheduled-for'
import { CoursePicker } from './course-picker'
import { UserPicker } from './user-picker'

const emptyRow = {
  user: undefined,
  course: undefined,
  scheduled_for: undefined
}

export function meta({}: Route.MetaArgs) {
  return [{ title: 'Adicionar matrícula' }]
}

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

  const submission: Promise<{ enrolled: Enrolled[] }> = submissionId
    ? req({
        url: `/orgs/${params.orgid}/enrollments/${submissionId}/submitted`,
        context,
        request
      }).then((r) => r.json())
    : Promise.resolve({ enrolled: [] })

  return { courses, submission }
}

export async function action({ params, request, context }: Route.ActionArgs) {
  const { orgid: org_id } = params
  const body = (await request.json()) as object

  const r = await req({
    url: `enrollments`,
    headers: new Headers({ 'Content-Type': 'application/json' }),
    method: HttpMethod.POST,
    body: JSON.stringify({ org_id, ...body }),
    request,
    context
  })

  const data = (await r.json()) as { sk: string }
  return redirect(`/${params.orgid}/enrollments/${data.sk}/submitted`)
}

export default function Route({
  loaderData: { courses, submission }
}: Route.ComponentProps) {
  const { orgid } = useParams()
  const { enrolled } = use(submission)
  const fetcher = useFetcher()
  const form = useForm({
    resolver: zodResolver(formSchema),
    defaultValues: { enrollments: [emptyRow] }
  })
  const { formState, control, handleSubmit, getValues, setValue } = form
  const { fields, insert, remove, append } = useFieldArray({
    control,
    name: 'enrollments'
  })

  const onSubmit = async (data: Schema) => {
    await fetcher.submit(JSON.stringify(data), {
      method: 'post',
      encType: 'application/json'
    })
  }

  const onSearch = async (search: string) => {
    const params = new URLSearchParams({ q: search })
    const r = await fetch(`/${orgid}/users.json?${params.toString()}`)
    const { hits } = (await r.json()) as { hits: User[] }
    return hits
  }

  const duplicateRow = (index: number, times: number = 1) => {
    if (fields.length + times > MAX_ITEMS) {
      return null
    }

    const { user, ...rest } = getValues(`enrollments.${index}`)
    Array.from({ length: times }, (_, i) => {
      // @ts-ignore
      insert(index + 1 + i, rest)
    })
  }

  useEffect(() => {
    if (enrolled.length === 0) {
      return
    }

    setValue(
      'enrollments',
      enrolled
        .filter(({ status }) => status === 'fail')
        .map(({ input_record }) => pick(['course', 'user'], input_record))
    )
  }, [enrolled, setValue])

  return (
    <div className="space-y-2.5">
      <Breadcrumb>
        <BreadcrumbList>
          <BreadcrumbItem>
            <BreadcrumbLink asChild>
              <Link to="../enrollments">Matrículas</Link>
            </BreadcrumbLink>
          </BreadcrumbItem>
          <BreadcrumbSeparator />
          <BreadcrumbItem>
            <BreadcrumbPage>Adicionar matrículas</BreadcrumbPage>
          </BreadcrumbItem>
        </BreadcrumbList>
      </Breadcrumb>

      <form
        onSubmit={handleSubmit(onSubmit)}
        className="lg:max-w-4xl mx-auto space-y-2.5"
        autoComplete="off"
        data-1p-ignore="true"
        data-lpignore="true"
      >
        <Card>
          <CardHeader>
            <CardTitle className="text-2xl">Adicionar matrículas</CardTitle>
            <CardDescription>
              Siga os passos abaixo para adicionar novas matrículas.
            </CardDescription>
            <CardAction>
              <ActionMenu />
            </CardAction>
          </CardHeader>

          <CardContent className="space-y-4">
            <div className="grid lg:grid-cols-[repeat(3,1fr)_auto] w-full gap-1.5">
              {/* Header */}
              <>
                <Cell>Colaborador</Cell>
                <Cell>Curso</Cell>
                <Cell>Matricular em</Cell>
                <Cell>{/**/}</Cell>
              </>

              {/* Rows */}
              <>
                {fields.map((field, index) => (
                  <Fragment key={field.id}>
                    {/* Separator only for mobile */}
                    {index >= 1 && <div className="h-2.5 lg:hidden"></div>}

                    <Controller
                      control={control}
                      name={`enrollments.${index}.user`}
                      render={({
                        field: { name, value, onChange },
                        fieldState
                      }) => (
                        <div className="grid gap-1">
                          <UserPicker
                            value={value}
                            onChange={onChange}
                            onSearch={onSearch}
                            fieldState={fieldState}
                          />

                          <ErrorMessage
                            errors={formState.errors}
                            name={name}
                            render={({ message }) => (
                              <p className="text-destructive text-sm">
                                {message}
                              </p>
                            )}
                          />
                        </div>
                      )}
                    />

                    <Controller
                      control={control}
                      name={`enrollments.${index}.course`}
                      render={({
                        field: { name, value, onChange },
                        fieldState
                      }) => (
                        <div className="grid gap-1">
                          <CoursePicker
                            value={value}
                            onChange={onChange}
                            options={courses}
                            error={fieldState.error}
                            readOnly
                          />

                          <ErrorMessage
                            errors={formState.errors}
                            name={name}
                            render={({ message }) => (
                              <p className="text-destructive text-sm">
                                {message}
                              </p>
                            )}
                          />
                        </div>
                      )}
                    />

                    <Controller
                      control={control}
                      name={`enrollments.${index}.scheduled_for`}
                      render={({ field: { value, onChange } }) => (
                        <ScheduledForInput value={value} onChange={onChange} />
                      )}
                    />

                    {/* Action */}
                    <div className="flex gap-1.5">
                      <Button
                        type="button"
                        tabIndex={-1}
                        variant="outline"
                        className="cursor-pointer"
                        onClick={() => duplicateRow(index)}
                        title="Duplicar linha"
                      >
                        <CopyIcon />
                      </Button>

                      <DuplicateRowMultipleTimes
                        index={index}
                        duplicateRow={duplicateRow}
                      />

                      <Button
                        tabIndex={-1}
                        variant="destructive"
                        className="cursor-pointer"
                        disabled={fields.length == 1}
                        onClick={() => remove(index)}
                      >
                        <Trash2Icon />
                      </Button>
                    </div>
                  </Fragment>
                ))}
              </>
            </div>

            <Button
              type="button"
              // @ts-ignore
              onClick={() => append(emptyRow)}
              className="cursor-pointer"
              disabled={fields.length == MAX_ITEMS}
              variant="outline"
              size="sm"
            >
              <PlusIcon /> Adicionar
            </Button>

            <Separator />

            <Button
              type="submit"
              className="cursor-pointer"
              disabled={formState.isSubmitting}
            >
              {formState.isSubmitting && <Spinner />}
              Matricular
            </Button>
          </CardContent>
        </Card>
      </form>
    </div>
  )
}

function DuplicateRowMultipleTimes({
  index,
  duplicateRow
}: {
  index: number
  duplicateRow: (index: number, times: number) => void
}) {
  const [open, { toggle, set }] = useToggle()
  const isMobile = useIsMobile()

  return (
    <Popover open={open} onOpenChange={toggle}>
      <PopoverTrigger asChild>
        <Button
          tabIndex={-1}
          variant="outline"
          className="cursor-pointer"
          title="Duplicar várias vezes"
        >
          <CopyPlusIcon />
        </Button>
      </PopoverTrigger>

      <PopoverContent align={isMobile ? 'start' : 'end'} className="w-82">
        <form
          onSubmit={(e) => {
            e.stopPropagation()
            e.preventDefault()
            const formData = new FormData(e.currentTarget)
            const times = parseInt(formData.get('quantity') as string)
            duplicateRow(index, times)
            set(false)
          }}
        >
          <div className="space-y-2.5">
            <h4 className="leading-none font-medium">Duplicar várias vezes</h4>
            <p className="text-muted-foreground text-sm">
              Duplique o curso desta linha na quantidade desejada para agilizar
              o preenchimento.
            </p>

            <div className="grid grid-cols-3 items-center gap-4">
              <Label htmlFor="quantity">Quantidade</Label>
              <Input
                id="quantity"
                name="quantity"
                type="number"
                defaultValue="2"
                min="2"
                max={String(MAX_ITEMS - 1)}
                className="col-span-2"
              />
            </div>

            <div className="flex justify-end gap-2.5">
              <Button
                type="button"
                variant="link"
                tabIndex={-1}
                className="cursor-pointer dark:text-white text-black"
                onClick={() => set(false)}
              >
                Cancelar
              </Button>
              <Button type="submit" className="cursor-pointer">
                Duplicar
              </Button>
            </div>
          </div>
        </form>
      </PopoverContent>
    </Popover>
  )
}

function ActionMenu() {
  const { orgid } = useParams()
  const [open, { set }] = useToggle()
  const { data, runAsync, loading } = useRequest(
    async () => {
      const r = await fetch(`/~/api/orgs/${orgid}/enrollments/submissions`, {
        method: 'GET'
      })
      return (await r.json()) as { items: { sk: string }[] }
    },
    { manual: true }
  )

  return (
    <Popover
      open={open}
      onOpenChange={async (open) => {
        set(open)

        if (open && !data) {
          await runAsync()
        }
      }}
    >
      <PopoverTrigger asChild>
        <Button variant="ghost" className="cursor-pointer">
          <EllipsisIcon />
        </Button>
      </PopoverTrigger>
      <PopoverContent align="end" className="p-0 overflow-hidden w-56">
        <div className="border-b p-2 text-xs text-muted-foreground font-medium">
          Envios recentes
        </div>
        <Command className="rounded-none">
          <CommandList>
            <CommandGroup>
              {loading && (
                <CommandItem disabled>
                  <Spinner />
                </CommandItem>
              )}

              {data?.items?.map(({ sk }, index) => (
                <CommandItem asChild key={index}>
                  <Link
                    to={`../enrollments/${sk}/submitted`}
                    className="cursor-pointer"
                  >
                    <DateTime
                      options={{
                        hour: '2-digit',
                        minute: '2-digit',
                        second: '2-digit'
                      }}
                    >
                      {sk}
                    </DateTime>
                  </Link>
                </CommandItem>
              ))}

              {data?.items?.length === 0 && (
                <CommandEmpty>Nenhum envio ainda</CommandEmpty>
              )}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  )
}

export function Cell({ children }: { children?: ReactNode }) {
  return (
    <div className="max-lg:hidden text-foreground font-medium text-sm">
      {children}
    </div>
  )
}
