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

import { formatCNPJ } from '@brazilian-utils/brazilian-utils'
import {
  BuildingIcon,
  CalendarIcon,
  CheckIcon,
  PlusCircleIcon
} from 'lucide-react'
import { MeiliSearchFilterBuilder } from 'meilisearch-helper'
import { pick } from 'ramda'
import { Suspense, useState } from 'react'
import { Await, Outlet, useSearchParams } from 'react-router'

import { cloudflareContext } from '@repo/auth/context'
import { Abbr } from '@repo/ui/components/abbr'
import { DataTable, DataTableViewOptions } from '@repo/ui/components/data-table'
import { ExportMenu } from '@repo/ui/components/export-menu'
import { FacetedFilter } from '@repo/ui/components/faceted-filter'
import { RangeCalendarFilter } from '@repo/ui/components/range-calendar-filter'
import { SearchFilter } from '@repo/ui/components/search-filter'
import { SearchForm } from '@repo/ui/components/search-form'
import { Skeleton } from '@repo/ui/components/skeleton'
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar'
import { Badge } from '@repo/ui/components/ui/badge'
import { Button } from '@repo/ui/components/ui/button'
import { CommandItem } from '@repo/ui/components/ui/command'
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 { cn, initials } from '@repo/ui/lib/utils'
import { headers, sortings, statuses } from '@repo/ui/routes/enrollments/data'
import { createSearch } from '@repo/util/meili'

import { columns, type Enrollment } from './columns'

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

export async function loader({ context, request }: Route.LoaderArgs) {
  const cloudflare = context.get(cloudflareContext)
  const { searchParams } = new URL(request.url)
  const query = searchParams.get('q') || ''
  const org = searchParams.get('org') || ''
  const from = searchParams.get('from')
  const to = searchParams.get('to')
  const sort = searchParams.get('sort') || 'created_at:desc'
  const status = searchParams.get('status')
  const page = Number(searchParams.get('p')) + 1
  const hitsPerPage = Number(searchParams.get('perPage')) || 25

  let builder = new MeiliSearchFilterBuilder().where('is_test', 'exists', false)

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

  if (org) {
    const { id } = JSON.parse(org)
    builder = builder.where('org_id', '=', id)
  }

  if (from && to) {
    const [field, from_] = from.split(':')
    builder = builder.where(field, 'between', [from_, to])
  }

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

  return {
    enrollments
  }
}

const formatted = new Intl.DateTimeFormat('en-CA', {
  year: 'numeric',
  month: '2-digit',
  day: '2-digit'
})

export default function Route({
  loaderData: { enrollments }
}: Route.ComponentProps) {
  const [searchParams, setSearchParams] = useSearchParams()
  const [selectedRows, setSelectedRows] = useState<Enrollment[]>([])
  const status = searchParams.get('status')
  const dateRange = useRangeParams()
  const org = searchParams.has('org')
    ? JSON.parse(searchParams.get('org') || '')
    : null

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

  return (
    <Suspense fallback={<Skeleton />}>
      <div className="space-y-0.5 mb-8">
        <h1 className="text-2xl font-bold tracking-tight">Matrículas</h1>
      </div>

      <Await resolve={enrollments}>
        {({ hits, page = 1, hitsPerPage, totalHits }) => (
          <DataTable
            sort={[{ id: 'created_at', desc: true }]}
            columnPinning={{ left: ['select'], right: ['action'] }}
            columns={columns}
            data={hits as Enrollment[]}
            pageIndex={page - 1}
            pageSize={hitsPerPage}
            setSelectedRows={setSelectedRows}
            rowCount={totalHits}
            columnVisibilityInit={{
              created_at: true,
              completed_at: false,
              started_at: false,
              failed_at: false,
              canceled_at: false
            }}
          >
            <div className="flex gap-2.5 mb-2.5">
              {selectedRows.length ? (
                <>
                  <div className="flex gap-2.5 items-center">
                    <ExportMenu
                      name="enrollments"
                      headers={headers}
                      selectedRows={selectedRows}
                    />
                  </div>
                </>
              ) : (
                <>
                  <div className="w-full 2xl:w-1/3">
                    <SearchForm
                      defaultValue={searchParams.get('q') || ''}
                      placeholder={
                        <>
                          Digite <Kbd className="border font-mono">/</Kbd> para
                          pesquisar
                        </>
                      }
                      onChange={(value) =>
                        setSearchParams((searchParams) => {
                          searchParams.set('q', String(value))
                          searchParams.delete('p')
                          return searchParams
                        })
                      }
                    />
                  </div>

                  <div className="flex gap-2.5 max-lg:flex-col w-full">
                    <div className="flex gap-2.5 max-lg:flex-col">
                      <FacetedFilter
                        title="Status"
                        icon={PlusCircleIcon}
                        className="lg:flex-1"
                        value={status ? status.split(',') : []}
                        onChange={(statuses) => {
                          setSearchParams((searchParams) => {
                            searchParams.delete('status')
                            searchParams.delete('p')

                            if (statuses.length) {
                              searchParams.set('status', statuses.join(','))
                            }

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

                      <RangeCalendarFilter
                        title="Período"
                        icon={CalendarIcon}
                        value={dateRange}
                        className="lg:flex-1"
                        options={Object.entries(sortings).map(
                          ([value, label]) => ({
                            value,
                            label
                          })
                        )}
                        onChange={(props) => {
                          setSearchParams((searchParams) => {
                            if (!props) {
                              searchParams.delete('from')
                              searchParams.delete('to')
                              return searchParams
                            }

                            const { rangeField, dateRange } = props

                            searchParams.set(
                              'from',
                              `${rangeField}:${formatted.format(dateRange?.from)}`
                            )
                            searchParams.set(
                              'to',
                              formatted.format(dateRange?.to)
                            )

                            return searchParams
                          })
                        }}
                      />

                      <SearchFilter
                        align="end"
                        requestOptions={{
                          manual: !org,
                          defaultParams: [org?.q || '']
                        }}
                        defaultValue={org?.q || ''}
                        onSearch={onSearch}
                        onChange={(value) => {
                          setSearchParams((searchParams) => {
                            if (value) {
                              searchParams.set(
                                'org',
                                JSON.stringify(pick(['id', 'name', 'q'], value))
                              )
                            } else {
                              searchParams.delete('org')
                            }

                            return searchParams
                          })
                        }}
                        render={({ id, name, cnpj, onSelect }) => (
                          <CommandItem
                            key={id}
                            value={id}
                            className="cursor-pointer"
                            onSelect={onSelect}
                          >
                            <div className="flex gap-2.5 items-start">
                              <Avatar className="size-10 hidden lg:block">
                                <AvatarFallback className="border">
                                  {initials(name)}
                                </AvatarFallback>
                              </Avatar>

                              <ul>
                                <li className="font-bold">
                                  <Abbr>{name}</Abbr>
                                </li>
                                <li className="text-muted-foreground text-sm">
                                  <Abbr>{formatCNPJ(cnpj)}</Abbr>
                                </li>
                              </ul>
                            </div>

                            <CheckIcon
                              className={cn(
                                'ml-auto',
                                org?.id === id ? 'opacity-100' : 'opacity-0'
                              )}
                            />
                          </CommandItem>
                        )}
                      >
                        {({ loading }) => (
                          <Button
                            variant="outline"
                            size="sm"
                            className="cursor-pointer border-dashed h-full"
                          >
                            {loading ? <Spinner /> : <BuildingIcon />}
                            <span>Empresa</span>
                            {org?.id ? (
                              <>
                                <Separator
                                  orientation="vertical"
                                  className="mx-0.5 h-4"
                                />

                                <Badge
                                  variant="outline"
                                  className="rounded-sm px-1 font-normal"
                                >
                                  <Abbr maxLen={14}>{org.name}</Abbr>
                                </Badge>
                              </>
                            ) : null}
                          </Button>
                        )}
                      </SearchFilter>
                    </div>

                    <div className="lg:ml-auto flex gap-2.5">
                      <DataTableViewOptions />
                    </div>
                  </div>
                </>
              )}
            </div>
          </DataTable>
        )}
      </Await>

      <Outlet />
    </Suspense>
  )
}

function useRangeParams() {
  const [searchParams] = useSearchParams()
  const [from, to] = [searchParams.get('from'), searchParams.get('to')]

  if (!from || !to) {
    return {}
  }

  const [rangeField, from_] = from.split(':')

  return {
    rangeField,
    dateRange: {
      from: new Date(from_),
      to: new Date(to)
    }
  }
}
