import { notFound } from "next/navigation"

import { AppShell } from "@/components/app-shell"
import { Badge } from "@/components/ui/badge"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { dayIn } from "@/lib/calendar"
import { standingOn } from "@/lib/cover"
import { translator } from "@/lib/i18n"
import { LEVEL_LABELS } from "@/lib/levels"
import { coversForPosition, standInsFor } from "@/lib/services/covers"
import { positionDetail, type PositionDuty } from "@/lib/services/positions"
import { requireAtLeast } from "@/lib/services/session"
import { DutySwitch } from "@/components/duty-switch"
import { OCCURRENCE_WORD } from "@/lib/i18n/duty-words"
import { Covers, type CoverLine } from "./covers"
import { JobDescription } from "./job-description"
import { toggleDuty } from "./actions"

/**
 * The position page, which `routes.json` calls "THE position page =
 * job-description editor and the only write surface".
 *
 * Everything a position is: who holds it, what the job is for, and the duties
 * it owes. Duties are shown active or not, because a deactivated duty is still
 * part of the job and hiding it here would leave nowhere to turn it back on.
 *
 * Admin, following ELEVA-139's narrower reading. The service is scoped per
 * viewer either way, so the day the client answers, this line is the change.
 */

/** The order a job is read in: what happens most often, first. */
const ORDER: PositionDuty["occurrence"][] = [
  "daily",
  "weekly",
  "monthly",
  "quarterly",
  "once",
  "general",
]

export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const user = await requireAtLeast("admin")
  const { id } = await params
  const position = await positionDetail(user, id)

  if (!position) notFound()

  /**
   * Today is read here, once, and handed down.
   *
   * It decides whether a cover is running or has not started, and the two end
   * differently. Read in the browser it would be the terminal's clock, which is
   * not the factory's: `dayIn` puts it in Bangkok, which is the calendar the
   * window's two ends were written against.
   */
  const today = dayIn(new Date())
  const [allCovers, standIns] = await Promise.all([
    coversForPosition(position.id),
    standInsFor(position.id),
  ])

  /**
   * The ones that have not finished, earliest first.
   *
   * `coversForPosition` returns every cover ever opened, newest window first,
   * and it is right to: months later somebody asks who was answering for this
   * job in some week. This card answers "who is answering for it now", so a
   * finished cover is dropped rather than shown as an answer to a question it
   * does not answer. Earliest first because the one running is the one that
   * started first, and it is the one somebody came here to read.
   */
  const covers: CoverLine[] = allCovers
    .flatMap((c) => {
      const standing = standingOn(
        { from: c.fromDate, until: c.untilDate },
        today,
      )
      if (standing === "over") return []
      return [
        {
          id: c.id,
          person: c.coveringPerson,
          personPosition: c.coveringPersonPosition,
          from: c.fromDate,
          until: c.untilDate,
          standing,
          reason: c.reason,
        },
      ]
    })
    .sort((a, b) => a.from.localeCompare(b.from))

  const t = translator(user.language)
  const groups = ORDER.map((occurrence) => ({
    occurrence,
    duties: position.duties.filter((d) => d.occurrence === occurrence),
  })).filter((g) => g.duties.length > 0)

  return (
    <AppShell title="position.title" user={user}>
      <div className="flex flex-col gap-4">
        <Card>
          <CardHeader>
            {/* The title is data, in Thai for many positions, and stays as the
                roster spells it. The level is ours and is translated. */}
            <CardTitle>{position.title}</CardTitle>
            <CardDescription>
              {position.department} · {LEVEL_LABELS[position.level]}
            </CardDescription>
          </CardHeader>
          {/*
            The holder and the stand-in in one card, separated by a rule rather
            than split across two. They answer one question between them, which
            is who owes this job's work, and a cover read anywhere but next to
            the name it replaces is a cover somebody misses.
          */}
          <CardContent className="flex flex-col gap-4">
            <div className="flex flex-col gap-1">
              <p className="text-sm text-foreground/80">
                {t("position.heldBy")}
              </p>
              {position.holders.length === 0 ? (
                <p className="text-foreground/70">{t("position.nobody")}</p>
              ) : (
                <ul className="flex flex-col gap-1">
                  {position.holders.map((h) => (
                    <li key={h.id}>
                      {h.name}{" "}
                      <span className="text-foreground/70 tabular-nums">
                        {h.employeeCode}
                      </span>
                    </li>
                  ))}
                </ul>
              )}
            </div>
            <div className="border-t pt-4">
              <Covers
                positionId={position.id}
                covers={covers}
                standIns={standIns}
              />
            </div>
          </CardContent>
        </Card>

        <Card>
          <CardHeader>
            <CardTitle>{t("position.jobDescription")}</CardTitle>
            <CardDescription>
              {t("position.jobDescriptionHint")}
            </CardDescription>
          </CardHeader>
          <CardContent>
            <JobDescription
              positionId={position.id}
              text={position.jobDescription}
            />
          </CardContent>
        </Card>

        {groups.length === 0 ? (
          <Card>
            <CardHeader>
              <CardTitle>{t("position.duties")}</CardTitle>
            </CardHeader>
            <CardContent>
              <p className="text-foreground/70">{t("position.noDuties")}</p>
            </CardContent>
          </Card>
        ) : (
          groups.map((group) => (
            <Card key={group.occurrence}>
              <CardHeader>
                <CardTitle>{t(OCCURRENCE_WORD[group.occurrence])}</CardTitle>
              </CardHeader>
              <CardContent className="flex flex-col gap-3">
                {group.duties.map((duty) => (
                  <div
                    key={duty.id}
                    className="flex flex-wrap items-start justify-between gap-3 border-b pb-3 last:border-0 last:pb-0"
                  >
                    <div className="flex flex-col gap-1">
                      <span className="flex items-center gap-2 font-medium">
                        {duty.description}
                        {duty.active ? null : (
                          <Badge variant="secondary">
                            {t("position.dutyOff")}
                          </Badge>
                        )}
                      </span>
                      {duty.checker ? (
                        <span className="text-sm text-foreground/80">
                          {t("position.checkedBy", { name: duty.checker })}
                        </span>
                      ) : null}
                    </div>
                    {/*
                      Bound to this duty here rather than passed as two ids and
                      reassembled in the browser: what the button may write is
                      fixed on the server, and `toggleDuty` asks the tier again
                      before the service asks about the position.
                    */}
                    <DutySwitch
                      active={duty.active}
                      toggle={toggleDuty.bind(null, position.id, duty.id)}
                    />
                  </div>
                ))}
              </CardContent>
            </Card>
          ))
        )}
      </div>
    </AppShell>
  )
}
