"use client"

import * as React from "react"
import type { DateRange } from "react-day-picker"

import { useLocale, useT } from "@/components/locale-provider"
import { ChoicePicker } from "@/components/choice-picker"
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { dateOf } from "@/lib/calendar"
import type { Choice } from "@/lib/choice"
import type { Standing } from "@/lib/cover"
import { calendarChrome, fullDay } from "@/lib/i18n/dates"
import type { MessageKey } from "@/lib/i18n/messages"
import type { CoverRefusal } from "@/lib/services/covers"
import { startCover, stopCover } from "./actions"

/**
 * Temporary cover, beside the holder.
 *
 * Deliberately small. Nobody asked for a covers management page, and
 * `/positions/[id]` is already the only write surface a position has, so
 * everything about who is answering for this job sits in one card: the holder,
 * then whoever is standing in for them.
 *
 * **A cover that has not started is not shown as one that has.** They are
 * different facts, and the End control does two different things to them: a
 * running cover is shortened to today, keeping every task already handed to the
 * stand-in, and one that has not started is deleted, because nothing was ever
 * generated from it. The badge says which, and the confirmation says what the
 * press will do.
 *
 * Covers that are over are not here. `coversForPosition` returns them, and it
 * is right to: months later somebody asks who was answering for this job in
 * some week. But the question this card answers is "who is answering for it
 * now", and a finished cover is not an answer to that. The day somebody needs
 * the history, it is one filter away and it is not this card.
 *
 * Nothing here imports a value from a service. `CoverRefusal` and `Standing`
 * cross as types, the rows cross as props, and the two writes cross through
 * `./actions`, which is where the Postgres driver stops.
 */

/** One cover, as the page hands it over: already narrowed, already dated. */
export type CoverLine = {
  id: string
  person: string
  /** The job the stand-in normally does. Null while they are between two. */
  personPosition: string | null
  from: string
  until: string
  /**
   * Decided on the server from Bangkok's today, and never in the browser. A
   * shared floor terminal's clock is not the factory's, and "has this started"
   * is exactly the question a wrong clock answers wrongly.
   */
  standing: Extract<Standing, "running" | "scheduled">
  reason: string | null
}

/** Every reason the service can refuse, and the sentence the reader gets. */
const REFUSAL_WORD: Record<CoverRefusal, MessageKey> = {
  // The same sentence the job-description editor says, because it is the same
  // fact and it already covers both halves of this key: the position is gone,
  // or it was never yours.
  position_out_of_scope: "position.notScoped",
  no_such_person: "cover.refusal.noSuchPerson",
  covers_own_position: "cover.refusal.ownPosition",
  window_ends_before_it_starts: "cover.refusal.endsBeforeItStarts",
  not_a_calendar_day: "cover.refusal.notACalendarDay",
  overlaps_an_existing_cover: "cover.refusal.overlaps",
}

/**
 * The picker's own local-midnight `Date` as the day it stands for.
 *
 * Through `dateOf` and never `toISOString`, which would be a timezone away from
 * the day under the cursor: press 1 August in Bangkok and store 31 July.
 */
function pickedDay(date: Date): string {
  return dateOf(date.getFullYear(), date.getMonth() + 1, date.getDate())
}

function EndCover({
  positionId,
  cover,
}: {
  positionId: string
  cover: CoverLine
}) {
  const t = useT()
  const [open, setOpen] = React.useState(false)
  const [pending, setPending] = React.useState(false)
  const [refused, setRefused] = React.useState<MessageKey | null>(null)

  async function go() {
    setPending(true)
    setRefused(null)
    try {
      const result = await stopCover(positionId, cover.id)
      if (result.ok) {
        setOpen(false)
        return
      }
      setRefused(
        result.reason === "no_such_cover"
          ? "cover.refusal.noSuchCover"
          : "position.notScoped",
      )
    } catch {
      setRefused("refusal.noAnswer")
    } finally {
      setPending(false)
    }
  }

  return (
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogTrigger render={<Button variant="outline" size="sm" />}>
        {t("cover.end")}
      </AlertDialogTrigger>
      <AlertDialogContent className="sm:max-w-md">
        <AlertDialogHeader>
          <AlertDialogTitle>{t("cover.end")}</AlertDialogTitle>
          <AlertDialogDescription>
            {cover.standing === "running"
              ? t("cover.endRunningConsequence")
              : t("cover.endScheduledConsequence")}
          </AlertDialogDescription>
        </AlertDialogHeader>
        {refused ? (
          <p className="text-sm text-destructive">{t(refused)}</p>
        ) : null}
        <AlertDialogFooter>
          <AlertDialogCancel>{t("action.cancel")}</AlertDialogCancel>
          <AlertDialogAction
            disabled={pending}
            onClick={(event) => {
              event.preventDefault()
              void go()
            }}
          >
            {pending ? t("action.working") : t("cover.end")}
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )
}

export function Covers({
  positionId,
  covers,
  standIns,
}: {
  positionId: string
  covers: CoverLine[]
  standIns: Choice[]
}) {
  const t = useT()
  const locale = useLocale()

  const [opening, setOpening] = React.useState(false)
  const [standIn, setStandIn] = React.useState("")
  const [range, setRange] = React.useState<DateRange | undefined>()
  const [reason, setReason] = React.useState("")
  const [pending, setPending] = React.useState(false)
  // A key and not a sentence, so the outcome of a write is read in the same
  // language as the rest of the page.
  const [outcome, setOutcome] = React.useState<MessageKey | null>(null)

  function reset() {
    setStandIn("")
    setRange(undefined)
    setReason("")
  }

  async function open() {
    setOutcome(null)
    if (!standIn) {
      setOutcome("cover.needStandIn")
      return
    }
    if (!range?.from) {
      setOutcome("cover.needDays")
      return
    }

    // One press picks one day, and a one-day cover is an ordinary thing: the
    // stand-in is there for the Tuesday somebody is at the hospital. Left
    // undefined, `to` would arrive as an empty string and be refused as not a
    // calendar day, which reads as the screen breaking.
    const from = pickedDay(range.from)
    const until = pickedDay(range.to ?? range.from)

    setPending(true)
    try {
      const result = await startCover(positionId, standIn, from, until, reason)
      if (result.ok) {
        reset()
        setOpening(false)
        setOutcome("cover.opened")
        return
      }
      setOutcome(REFUSAL_WORD[result.reason])
    } catch {
      setOutcome("refusal.noAnswer")
    } finally {
      setPending(false)
    }
  }

  return (
    <div className="flex flex-col gap-3">
      <p className="text-sm text-foreground/80">{t("cover.standingIn")}</p>

      {covers.length === 0 ? (
        <p className="text-foreground/70">{t("cover.nobody")}</p>
      ) : (
        <ul className="flex flex-col gap-3">
          {covers.map((cover) => (
            <li
              key={cover.id}
              className="flex flex-wrap items-start justify-between gap-3"
            >
              <div className="flex flex-col gap-1">
                <span className="flex flex-wrap items-center gap-2 font-medium">
                  {/* A name is data and stays as the roster spells it. */}
                  {cover.person}
                  <Badge
                    variant={
                      cover.standing === "running" ? "default" : "secondary"
                    }
                  >
                    {cover.standing === "running"
                      ? t("cover.inForce")
                      : t("cover.notStarted")}
                  </Badge>
                </span>
                {cover.personPosition ? (
                  <span className="text-sm text-foreground/70">
                    {t("cover.normally", { position: cover.personPosition })}
                  </span>
                ) : null}
                <span className="text-sm text-foreground/80">
                  {t("cover.window", {
                    from: fullDay(cover.from, locale),
                    until: fullDay(cover.until, locale),
                  })}
                </span>
                {cover.reason ? (
                  <span className="text-sm text-foreground/70">
                    {cover.reason}
                  </span>
                ) : null}
              </div>
              <EndCover positionId={positionId} cover={cover} />
            </li>
          ))}
        </ul>
      )}

      {outcome && !opening ? (
        <p className="text-sm text-foreground/80">{t(outcome)}</p>
      ) : null}

      {!opening ? (
        <div>
          <Button
            variant="outline"
            onClick={() => {
              setOutcome(null)
              setOpening(true)
            }}
          >
            {t("cover.open")}
          </Button>
        </div>
      ) : (
        <div className="flex flex-col gap-4">
          <Field>
            <FieldLabel htmlFor="cover-stand-in">
              {t("cover.standIn")}
            </FieldLabel>
            <ChoicePicker
              id="cover-stand-in"
              value={standIn}
              onChange={setStandIn}
              choices={standIns}
              placeholder={t("cover.standInPlaceholder")}
              empty={t("cover.standInEmpty")}
            />
            <FieldDescription>{t("cover.standInHint")}</FieldDescription>
          </Field>

          <Field>
            <FieldLabel>{t("cover.days")}</FieldLabel>
            <FieldDescription>{t("cover.daysHint")}</FieldDescription>
            <Calendar
              mode="range"
              selected={range}
              onSelect={setRange}
              numberOfMonths={2}
              defaultMonth={range?.from ?? new Date()}
              // Without these the picker names its months and its seven column
              // headers from date-fns, whose Thai does not use the Buddhist era:
              // 2026 under a screen that says 2569 everywhere else.
              formatters={calendarChrome(locale)}
              className="rounded-lg border"
            />
            <p className="text-sm text-foreground/80">
              {range?.from
                ? t("cover.window", {
                    from: fullDay(pickedDay(range.from), locale),
                    until: fullDay(pickedDay(range.to ?? range.from), locale),
                  })
                : t("cover.daysNone")}
            </p>
          </Field>

          <Field>
            <FieldLabel htmlFor="cover-reason">{t("cover.reason")}</FieldLabel>
            <Input
              id="cover-reason"
              value={reason}
              onChange={(e) => setReason(e.target.value)}
              placeholder={t("cover.reasonPlaceholder")}
            />
          </Field>

          {outcome ? (
            <p className="text-sm text-destructive">{t(outcome)}</p>
          ) : null}

          <div className="flex gap-2">
            <Button disabled={pending} onClick={() => void open()}>
              {pending ? t("action.working") : t("cover.start")}
            </Button>
            <Button
              variant="outline"
              disabled={pending}
              onClick={() => {
                reset()
                setOutcome(null)
                setOpening(false)
              }}
            >
              {t("action.cancel")}
            </Button>
          </div>
        </div>
      )}
    </div>
  )
}
