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

import { ErrorMessage } from '@hookform/error-message'
import { zodResolver } from '@hookform/resolvers/zod'
import {
  CircleQuestionMarkIcon,
  CopyIcon,
  PlusIcon,
  Trash2Icon
} from 'lucide-react'
import { Fragment, useMemo } from 'react'
import { Controller, useFieldArray, useForm, useWatch } from 'react-hook-form'
import { Link, redirect, useFetcher, useParams } from 'react-router'

import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator
} from '@repo/ui/components/ui/breadcrumb'
import { Button } from '@repo/ui/components/ui/button'
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle
} from '@repo/ui/components/ui/card'
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger
} from '@repo/ui/components/ui/hover-card'
import { Kbd } from '@repo/ui/components/ui/kbd'
import { Separator } from '@repo/ui/components/ui/separator'
import { Spinner } from '@repo/ui/components/ui/spinner'
import { HttpMethod, request as req } from '@repo/util/request'

import { workspaceContext } from '@/middleware/workspace'
import { CoursePicker } from '../_.$orgid.enrollments.add/course-picker'
import {
  formSchema,
  MAX_ITEMS,
  type Course,
  type Schema,
  type User
} from '../_.$orgid.enrollments.add/data'
import {
  Cell,
  DuplicateRowMultipleTimes,
  emptyRow
} from '../_.$orgid.enrollments.add/route'
import { ScheduledForInput } from '../_.$orgid.enrollments.add/scheduled-for'
import { UserPicker } from '../_.$orgid.enrollments.add/user-picker'

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

type Seat = {
  order_id: string
  enrollment_id: string
}

export async function loader({ request, params, context }: Route.LoaderArgs) {
  const { subscription } = context.get(workspaceContext)
  // If there's subscription for the org, redirect it
  if (subscription) {
    throw redirect('../enrollments/add')
  }

  const seats = await req({
    url: `/orgs/${params.orgid}/seats`,
    request,
    context
  })
    .then((r) => r.json() as any)
    .then(({ items }) => items as { sk: string; course: Course }[])

  return { seats }
}

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(`/${org_id}/enrollments/${data.sk}/submitted`)
}

export default function Route({ loaderData: { seats } }: Route.ComponentProps) {
  const { orgid } = useParams()
  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 enrollments = useWatch({
    control,
    name: 'enrollments'
  })

  const usedSeatIds = useMemo(() => {
    return new Set(enrollments?.map((e) => e.id).filter(Boolean))
  }, [enrollments])

  const seatsByCourse = useMemo(() => {
    return seats.reduce<Record<string, Seat[]>>((acc, seat) => {
      const courseId = seat.course.id
      const [, order_id, , enrollment_id] = seat.sk.split('#')

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

      acc[courseId].push({ order_id, enrollment_id })
      return acc
    }, {})
  }, [seats])

  const usedSeatsByCourse = useMemo(() => {
    const acc = new Map<string, number>()

    seats.forEach((seat) => {
      const [, , , enrollment_id] = seat.sk.split('#')

      if (!usedSeatIds.has(enrollment_id)) {
        return
      }

      const courseId = seat.course.id
      acc.set(courseId, (acc.get(courseId) ?? 0) + 1)
    })

    return acc
  }, [seats, usedSeatIds])

  const courses = useMemo(() => {
    return {
      hits: Array.from(
        seats
          .reduce((acc, { course }) => {
            const existing = acc.get(course.id)

            if (existing) {
              existing.quantity += 1
            } else {
              acc.set(course.id, {
                ...course,
                metadata__unit_price: 1,
                quantity: 1,
                disabled: false
              })
            }

            return acc
          }, new Map<string, any>())
          .values()
      ).map((course) => {
        const used = usedSeatsByCourse.get(course.id) ?? 0
        return { ...course, disabled: used >= course.quantity }
      })
    }
  }, [seats, usedSeatsByCourse])

  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 pickSeat = (courseId: string): Seat | null => {
    const pool = seatsByCourse[courseId]

    if (!pool) {
      return null
    }

    return (
      pool.find((seat) => {
        return !usedSeatIds.has(seat.enrollment_id)
      }) ?? null
    )
  }

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

    const { course } = getValues(`enrollments.${index}`)

    if (!course?.id) {
      Array.from({ length: times }, (_, i) => {
        // @ts-ignore
        insert(index + 1 + i, { course: null })
      })

      return
    }

    const reservedSeatIds = new Set(usedSeatIds)

    Array.from({ length: times }, (_, i) => {
      const pool = seatsByCourse[course.id]

      const seat =
        pool?.find((seat) => !reservedSeatIds.has(seat.enrollment_id)) ?? null

      if (seat) {
        reservedSeatIds.add(seat.enrollment_id)

        // @ts-ignore
        insert(index + 1 + i, {
          id: seat.enrollment_id,
          seat: { order_id: seat.order_id },
          course
        })
      } else {
        // @ts-ignore
        insert(index + 1 + i, { course: null })
      }
    })
  }

  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 className="lg:max-w-4xl mx-auto">
          <CardHeader>
            <CardTitle className="text-2xl">Adicionar matrículas</CardTitle>
            <CardDescription>
              Siga os passos abaixo para adicionar colaboradores às matrículas
              abertas.
            </CardDescription>
          </CardHeader>

          <CardContent className="space-y-4">
            <div className="grid lg:grid-cols-[repeat(3,1fr)_auto] w-full gap-3">
              {/* Header */}
              <>
                <Cell>Colaborador</Cell>
                <Cell>Curso</Cell>
                <Cell className="flex items-center gap-1.5">
                  Matricular em
                  <HoverCard openDelay={0}>
                    <HoverCardTrigger asChild>
                      <button type="button">
                        <CircleQuestionMarkIcon className="size-4 text-muted-foreground" />
                      </button>
                    </HoverCardTrigger>
                    <HoverCardContent
                      align="end"
                      className="text-sm space-y-1.5 lg:w-76"
                    >
                      <p>
                        Escolha a data em que o colaborador será matriculado no
                        curso.
                      </p>

                      <p>
                        Você poderá acompanhar as matrículas em{' '}
                        <Kbd>Agendamentos</Kbd>
                      </p>
                    </HoverCardContent>
                  </HoverCard>
                </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={(course) => {
                              const seat = pickSeat(course.id)

                              if (!seat) {
                                return
                              }

                              setValue(
                                `enrollments.${index}.id`,
                                seat.enrollment_id
                              )

                              setValue(
                                `enrollments.${index}.seat.order_id`,
                                seat.order_id
                              )

                              onChange(course)
                            }}
                            options={courses.hits}
                            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>

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

            <Separator />

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