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

import Fuse from 'fuse.js'
import { DateTime } 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 { 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
  }).then((r) => r.json())

  const [startDate, endDate] = billingPeriod(
    subscription?.billing_day || 1,
    DateTime.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 {
    subscription,
    billing,
    startDate: DateTime.fromISO(start, { zone: tz }).toJSDate(),
    endDate: DateTime.fromISO(end, { zone: tz }).toJSDate()
  }
}

export default function Route({
  loaderData: { subscription, 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={subscription?.billing_day || 1}
                    />

                    <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 (
    <div className="border rounded-lg overflow-hidden">
      <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.format(new Date(enrolled_at))}
                    </TableCell>
                    <TableCell>{currency.format(unit_price)}</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.format(new Date(created_at))}
                    </TableCell>
                    <TableCell>{currency.format(unit_price)}</TableCell>
                  </TableRow>
                )
              )}
            </TableBody>
          </>
        ) : null}

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

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

const datetime = new Intl.DateTimeFormat('pt-BR', {
  day: '2-digit',
  month: '2-digit',
  year: 'numeric',
  hour: '2-digit',
  minute: '2-digit'
})

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