import Link from "next/link"

import { AppShell } from "@/components/app-shell"
import { Badge } from "@/components/ui/badge"
import {
  Item,
  ItemContent,
  ItemDescription,
  ItemGroup,
  ItemTitle,
} from "@/components/ui/item"
import { translator } from "@/lib/i18n"
import { moment } from "@/lib/i18n/dates"
import {
  markShownRead,
  notificationFeed,
  type NotificationRow,
} from "@/lib/services/notifications"
import { requireUser } from "@/lib/services/session"

/**
 * What somebody has been told.
 *
 * ## Why this is a server component and not a table
 *
 * There is nothing to sort, filter or select. Every other list here is a
 * `DataTable` because a manager reading forty rows of their department's work
 * needs to cut it down; this is one person's four notifications, newest first,
 * and the interaction is pressing one. Rendering it on the server means the
 * words, the dates and the Buddhist-era calendar are chosen where the reader's
 * language already is, and nothing about it reaches the browser.
 *
 * ## Opening the list is what marks it read
 *
 * The write happens here, after the read and before anything is drawn. The
 * reasoning is in `markShownRead`, and the two properties that make it safe to
 * do during a render are that it only touches the ids just read and that it
 * writes nothing the second time. The rows still draw as new on this render,
 * which is the point: the person sees which ones they had not seen, and the
 * badge is gone by the time the sidebar beside them is drawn.
 */
export default async function Page() {
  const user = await requireUser()
  const rows = await notificationFeed(user)
  await markShownRead(
    user.personId,
    rows.filter((r) => !r.read).map((r) => r.id),
  )

  const t = translator(user.language)

  return (
    <AppShell title="nav.notifications" user={user}>
      {rows.length === 0 ? (
        <p className="text-foreground/80">{t("notifications.empty")}</p>
      ) : (
        <ItemGroup className="gap-2">
          {rows.map((row) => (
            <Row key={row.id} row={row} locale={user.language} t={t} />
          ))}
        </ItemGroup>
      )}
    </AppShell>
  )
}

function Row({
  row,
  locale,
  t,
}: {
  row: NotificationRow
  locale: Parameters<typeof moment>[1]
  t: ReturnType<typeof translator>
}) {
  const body = (
    <>
      <ItemContent>
        <ItemTitle className="line-clamp-none">
          {t(row.key, { name: row.name })}
        </ItemTitle>
        {/*
          The task, on its own line, because the sentence above deliberately
          does not name it: a row that said "Somchai finished Check the oil
          level" would be one long line on a phone with the only pressable part
          buried in the middle of it.
        */}
        {row.task ? <ItemDescription>{row.task}</ItemDescription> : null}
        {/*
          Their words, not ours. A send-back and a reminder are worth reading
          here rather than only after opening the task, because the whole reason
          the note is required is that somebody has to act on it.
        */}
        {row.note ? (
          <ItemDescription className="border-l-2 border-border pl-3 italic">
            {row.note}
          </ItemDescription>
        ) : null}
      </ItemContent>
      <div className="flex shrink-0 flex-col items-end gap-1">
        {!row.read ? <Badge>{t("notifications.new")}</Badge> : null}
        <span className="text-xs whitespace-nowrap text-muted-foreground tabular-nums">
          {moment(row.at, locale)}
        </span>
      </div>
    </>
  )

  // A notification with no task behind it cannot happen from anything v1 sends,
  // and it is drawn rather than hidden: an event type added later that carries
  // no task must show up as a row somebody can read, not as a link to nowhere.
  return row.taskId ? (
    <Item
      variant="outline"
      className="items-start hover:bg-accent/50"
      render={<Link href={`/tasks/${row.taskId}`} />}
    >
      {body}
    </Item>
  ) : (
    <Item variant="outline" className="items-start">
      {body}
    </Item>
  )
}
