import { AppShell } from "@/components/app-shell"
import { translator, type Translate } from "@/lib/i18n"
import { dayAndMonth, fullDay } from "@/lib/i18n/dates"
import { percentDone } from "@/lib/kpi-view"
import { departmentKpi } from "@/lib/services/kpi"
import { listDepartmentNames } from "@/lib/services/positions"
import { requireUser } from "@/lib/services/session"
import { DepartmentPicker } from "@/app/team/department-picker"
import { KpiTable } from "./kpi-table"

/**
 * The measures, over the last thirty days.
 *
 * ## Who this screen is for
 *
 * Everybody, and that is the whole feature. ELEVA-116 is "a person is evaluated
 * on KPI they cannot see", and the answer is not a manager's screen with a
 * gentler tone: it is that whatever a manager reads about somebody, that person
 * reads about themselves, from the same service, in the same words. So the
 * guard here is `requireUser` and not `requireAtLeast("manager")`. A staff
 * member's scope is themselves, decided in the service; the four figures at the
 * top are their row, and there is no table under them because it would be the
 * same row written out twice.
 *
 * ## Why each figure says what it counts
 *
 * A percentage with no definition beside it is a number people argue with
 * rather than act on, and the argument falls on whoever the number is about.
 * Every tile carries one sentence saying what it counted, in the language of
 * the person reading it, on the screen rather than in a document somebody has
 * to be sent.
 *
 * ## What is deliberately absent
 *
 * No chart, and nothing coloured. /team paints its late count red because it is
 * a queue somebody works through this morning; this is a month's measure of
 * people, and a red number is a verdict the data has not earned. The figures
 * are plain, the table sorts on any column, and a manager who wants the worst
 * first presses a header.
 *
 * There is also no figure about how long work takes. `time_to_do_minutes` is
 * unset on every duty in the database, and a total built from it would be an
 * invention wearing a number's clothes. See the header of the service.
 */
export default async function Page({
  searchParams,
}: {
  searchParams: Promise<Record<string, string | string[] | undefined>>
}) {
  const user = await requireUser()

  const params = await searchParams
  /*
    Repeated rather than comma separated, the shape /team and /positions already
    read, because a department name may contain a comma and because three
    screens should not disagree about what `?dept=` means. Handed straight to
    the service, which drops it for anybody below admin.
  */
  const dept = Array.isArray(params.dept)
    ? params.dept
    : params.dept
      ? [params.dept]
      : []

  const [kpi, departmentNames] = await Promise.all([
    departmentKpi(user, { departments: dept }),
    // Only an admin is offered a choice, so only an admin costs a second query.
    user.tier === "admin" ? listDepartmentNames(user) : Promise.resolve([]),
  ])

  const t = translator(user.language)

  return (
    <AppShell title="kpi.title" user={user}>
      <div className="flex flex-col gap-4">
        <div className="flex flex-wrap items-center justify-between gap-3">
          {/*
            The department names are data and stay as the roster spells them.
            The window is beside them because every figure below is cut on it,
            and a measure whose period is not on screen is a measure somebody
            reads against the wrong month.

            The far end in full, the near end short. The window always closes
            today, and today is the date worth writing out: it carries the year,
            which for a Thai reader is 2569 and not 2026. The other end is a day
            and a month, because a window that is thirty days long cannot cross
            two years and printing it twice would say nothing new.
          */}
          <p className="text-foreground/80">
            {kpi
              ? [
                  kpi.departments.join(", "),
                  `${dayAndMonth(kpi.from, user.language)} → ${fullDay(kpi.to, user.language)}`,
                ]
                  .filter((part) => part !== "")
                  .join(" · ")
              : null}
          </p>
          {user.tier === "admin" ? (
            <DepartmentPicker departments={departmentNames} />
          ) : null}
        </div>

        {!kpi ? (
          /*
            No department, which happens two ways: an admin who has cleared the
            picker and is one click from an answer, and a manager between
            positions for whom nothing here can be made to work until HR acts.
            Both sentences already exist on /team, which refuses for the same
            two reasons.
          */
          <p className="text-foreground/80">
            {user.tier === "admin"
              ? t("team.chooseDepartment")
              : t("team.noDepartment")}
          </p>
        ) : !kpi.overall ? (
          /*
            Nothing fell due at all. A row of noughts would read as a month of
            failure, which is the opposite of what an empty plate means: `ram`
            holds a position in a department outside the pilot and has never
            been given a task.
          */
          <p className="text-foreground/80">
            {t(kpi.scope === "self" ? "kpi.nothingDueSelf" : "kpi.nothingDue")}
          </p>
        ) : (
          <>
            <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
              <Tile
                label={t("kpi.completion")}
                figure={`${percentDone(kpi.overall)}%`}
                count={t("kpi.completionOf", {
                  done: kpi.overall.done,
                  due: kpi.overall.due,
                })}
                means={t("kpi.completionMeans")}
              />
              <Tile
                label={t("kpi.late")}
                figure={String(kpi.overall.late)}
                count={t("kpi.ofDue", { due: kpi.overall.due })}
                means={t("kpi.lateMeans")}
              />
              <Tile
                label={t("kpi.missed")}
                figure={String(kpi.overall.missed)}
                count={t("kpi.ofDue", { due: kpi.overall.due })}
                means={t("kpi.missedMeans")}
              />
              {/*
                The one figure that can be missing while the others stand: a
                department can have thirty days of work behind it and nothing
                approved yet. Then there is no number, only the sentence saying
                so, which is what /team's completion tile does in the same
                situation. A placeholder glyph in a figure's place is a number
                somebody squints at.
              */}
              <Tile
                label={t("kpi.checkWait")}
                figure={kpi.checkWait ? t("kpi.hours", kpi.checkWait) : null}
                count={checkWaitCount(t, kpi.checkWait)}
                means={t("kpi.checkWaitMeans")}
              />
            </div>

            {/*
              Only where there is somebody else in it. A staff member's table
              would hold one row saying what the four tiles above already say.
            */}
            {kpi.people.length > 0 ? (
              <KpiTable rows={kpi.people} empty={t("kpi.nothingDue")} />
            ) : null}
          </>
        )}
      </div>
    </AppShell>
  )
}

/**
 * One figure: what it is called, what it is, what it counted, what it means.
 *
 * The last line is not decoration and not help text. It is the sentence the
 * person being measured needs in order to know what the number above it was
 * counting, which is the difference between a measure and an accusation.
 */
function Tile({
  label,
  figure,
  count,
  means,
}: {
  label: string
  /** Null when there is nothing to put here, and then `count` says why. */
  figure: string | null
  count: string
  means: string
}) {
  return (
    <div className="flex flex-col gap-1 rounded-xl border bg-card p-4">
      <span className="text-sm text-foreground/70">{label}</span>
      {figure ? (
        <span className="text-3xl leading-none font-semibold tabular-nums">
          {figure}
        </span>
      ) : null}
      <span className="min-h-5 text-sm text-foreground/70">{count}</span>
      <span className="text-sm text-foreground/60">{means}</span>
    </div>
  )
}

/**
 * "half of 12 approved waited less" is a middle. "half of 1" is not, and there
 * is a real department with three approved tasks in the window, so one is not a
 * corner this dictionary can decline to have.
 */
function checkWaitCount(
  t: Translate,
  wait: { from: number; hours: number } | null,
): string {
  if (!wait) return t("kpi.checkWaitNone")
  return wait.from === 1
    ? t("kpi.checkWaitFromOne")
    : t("kpi.checkWaitFrom", { n: wait.from })
}
