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

import Fuse from 'fuse.js'
import { DateTime as LuxonDateTime } from 'luxon'
import { Suspense, useMemo } from 'react'
import { BanIcon } from 'lucide-react'
import { Await, useSearchParams } from 'react-router'

import { cn } from '@repo/ui/lib/utils'
import { request as req } from '@repo/util/request'
import { Skeleton } from '@repo/ui/components/skeleton'
import { Card, CardContent } from '@repo/ui/components/ui/card'
import {
  Table,
  TableBody,
  TableCell,
  TableFooter,
  TableHead,
  TableHeader,
  TableRow
} from '@repo/ui/components/ui/table'
import { Abbr } from '@repo/ui/components/abbr'
import { Button } from '@repo/ui/components/ui/button'
import { SearchForm } from '@repo/ui/components/search-form'
import {
  Empty,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle
} from '@repo/ui/components/ui/empty'
import { Kbd } from '@repo/ui/components/ui/kbd'
import { Currency } from '@repo/ui/components/currency'
import { DateTime } from '@repo/ui/components/datetime'

import { billingPeriod, formatDate } from './util'
import { RangePeriod } from './range-period'
import { tz, statuses } from './data'

export function meta({}) {
  return [{ title: 'Resumo de cobranças' }]
}

export async function loader({ context, request, params }: Route.LoaderArgs) {
  const { searchParams } = new URL(request.url)
  const subscription = await req({
    url: `/orgs/${params.orgid}/subscription`,
    context,
    request
  })

  const { billing_day = 1 } = (await subscription.json()) as {
    billing_day: number
  }

  const [startDate, endDate] = billingPeriod(
    billing_day,
    LuxonDateTime.now().setZone(tz).toJSDate()
  )
  const start = searchParams.get('start') || formatDate(startDate)
  const end = searchParams.get('end') || formatDate(endDate)

  const billing = req({
    url: `/orgs/${params.orgid}/billing?start_date=${start}&end_date=${end}`,
    context,
    request
  }).then((r) => r.json())

  return {
    billing_day,
    billing,
    startDate: LuxonDateTime.fromISO(start, { zone: tz }).toJSDate(),
    endDate: LuxonDateTime.fromISO(end, { zone: tz }).toJSDate()
  }
}

export default function Route({
  loaderData: { billing_day, billing, startDate, endDate }
}: 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">
            Resumo de cobranças
          </h1>
          <p className="text-muted-foreground">
            Acompanhe as cobranças em tempo real e garanta mais eficiência no
            controle financeiro.
          </p>
        </div>

        <Await resolve={billing}>
          {({ items = [], ...billing }) => {
            const {
              icon: Icon,
              label: status,
              color
            } = statuses?.[billing?.status || 'CLOSED']

            return (
              <Card>
                <CardContent className="space-y-4">
                  <div className="flex max-lg:flex-col gap-2.5">
                    <div className="w-full xl:w-1/4">
                      <SearchForm
                        defaultValue={search || ''}
                        placeholder={
                          <>
                            Digite <Kbd className="border font-mono">/</Kbd>{' '}
                            para pesquisar
                          </>
                        }
                        onChange={(value) =>
                          setSearchParams((searchParams) => {
                            searchParams.set('s', String(value))
                            return searchParams
                          })
                        }
                      />
                    </div>

                    <RangePeriod
                      startDate={startDate}
                      endDate={endDate}
                      billingDay={billing_day}
                    />

                    <Button
                      className={cn('pointer-events-none lg:ml-auto', color)}
                      variant="outline"
                      asChild
                    >
                      <span>
                        <Icon className="size-3.5" /> {status}
                      </span>
                    </Button>
                  </div>

                  <List items={items} search={search} />
                </CardContent>
              </Card>
            )
          }}
        </Await>
      </Suspense>
    </>
  )
}

function List({ items, search }) {
  const fuse = useMemo(() => {
    return new Fuse(items, {
      keys: ['user.name'],
      threshold: 0.3,
      includeMatches: true
    })
  }, [items])

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

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

  const charges = filtered
    ?.filter((item) => 'course' in item && item?.unit_price > 0)
    ?.sort(sortBy('enrolled_at'))
  const credits = filtered
    ?.filter((item) => 'course' in item && item?.unit_price < 0)
    ?.sort(sortBy('created_at'))

  if (items.length === 0) {
    return (
      <Empty className="border border-dashed">
        <EmptyHeader>
          <EmptyMedia variant="icon">
            <BanIcon />
          </EmptyMedia>
          <EmptyTitle>Nenhuma cobrança encontrada</EmptyTitle>
          <EmptyDescription>
            Não há nenhuma cobrança para este período.
          </EmptyDescription>
        </EmptyHeader>
      </Empty>
    )
  }

  if (filtered.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 (
    <Table className="table-auto w-full">
      {charges.length ? (
        <>
          <TableHeader>
            <TableRow className="bg-muted-foreground/10 pointer-events-none">
              <TableHead>Colaborador</TableHead>
              <TableHead>Curso</TableHead>
              <TableHead>Matriculado por</TableHead>
              <TableHead>Matriculado em</TableHead>
              <TableHead>Valor unit.</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {charges?.map(
              (
                { user, course, author: created_by, unit_price, enrolled_at },
                index
              ) => (
                <TableRow key={index}>
                  <TableCell>
                    <Abbr>{user.name}</Abbr>
                  </TableCell>
                  <TableCell>
                    <Abbr>{course.name}</Abbr>
                  </TableCell>
                  <TableCell>
                    <Abbr>{created_by ? created_by.name : 'N/A'}</Abbr>
                  </TableCell>
                  <TableCell>
                    <DateTime>{enrolled_at}</DateTime>
                  </TableCell>
                  <TableCell>
                    <Currency>{unit_price}</Currency>
                  </TableCell>
                </TableRow>
              )
            )}
          </TableBody>
        </>
      ) : null}

      {credits.length ? (
        <>
          <TableHeader>
            <TableRow className="bg-muted-foreground/10 pointer-events-none border-t">
              <TableHead colSpan={2}></TableHead>
              <TableHead>Cancelado por</TableHead>
              <TableHead colSpan={2}>Cancelado em</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {credits?.map(
              (
                { user, course, author: canceled_by, unit_price, created_at },
                index
              ) => (
                <TableRow key={index}>
                  <TableCell>
                    <Abbr>{user.name}</Abbr>
                  </TableCell>
                  <TableCell>
                    <Abbr>{course.name}</Abbr>
                  </TableCell>
                  <TableCell>
                    <Abbr>{canceled_by ? canceled_by.name : 'N/A'}</Abbr>
                  </TableCell>
                  <TableCell>
                    <DateTime>{created_at}</DateTime>
                  </TableCell>
                  <TableCell>
                    <Currency>{unit_price}</Currency>
                  </TableCell>
                </TableRow>
              )
            )}
          </TableBody>
        </>
      ) : null}

      <TableFooter>
        <TableRow>
          <TableCell colSpan={4} className="text-right pointer-events-none">
            Total
          </TableCell>
          <TableCell>
            <Currency>
              {filtered
                ?.filter((x) => 'course' in x)
                ?.reduce((acc, { unit_price }) => acc + unit_price, 0)}
            </Currency>
          </TableCell>
        </TableRow>
      </TableFooter>
    </Table>
  )
}

const sortBy = (field: 'enrolled_at' | 'created_at') => (a: any, b: any) =>
  new Date(a[field]).getTime() - new Date(b[field]).getTime()
