"use client"

import * as React from "react"
import { usePathname, useRouter, useSearchParams } from "next/navigation"

import { useT } from "@/components/locale-provider"
import { Button } from "@/components/ui/button"
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
} from "@/components/ui/input-group"
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"
import { LATE_SORTS, type LateSort } from "@/lib/team-view"
import type { MessageKey } from "@/lib/i18n/messages"
import {
  IconLayersSubtract,
  IconListCheck,
  IconUser,
  IconX,
} from "@tabler/icons-react"

/**
 * One bar over whichever table is on screen.
 *
 * The url is the state, the same idiom `components/positions-toolbar.tsx`
 * established: typing replaces after a pause, so Back does not undo letters one
 * at a time, and the controls that are a choice rather than a phrase push, so
 * Back restores the previous one. A narrowed view is a link somebody can send.
 *
 * Two fields and not two pickers. A picker would apply on release; these apply
 * as somebody types, which is what makes a bar worth having on a shared
 * terminal where the reader is standing up. The names and the duties actually
 * in the rows hang off each field as suggestions, so a Thai name can be chosen
 * rather than typed: 183 of the 193 people are spelled in a script that is slow
 * to enter on a factory keyboard.
 *
 * Sort and the grouping toggle are here only for the Late tab. They mean
 * nothing on the other two, whose order is already the thing somebody scans
 * them for, and a control that does nothing is worse than one that is absent.
 */

const SORT_LABEL: Record<LateSort, MessageKey> = {
  oldest: "team.sortOldest",
  most: "team.sortMost",
  person: "team.sortPerson",
}

export function TeamFilters({
  people,
  duties,
  sort,
  grouped,
  lateTab,
  shown,
}: {
  /** Every person in the tab's rows before the filter, so a choice is reversible. */
  people: string[]
  duties: string[]
  sort: LateSort
  grouped: boolean
  lateTab: boolean
  /** What the table under this bar is showing, already worded by the page. */
  shown: string
}) {
  const t = useT()
  const router = useRouter()
  const pathname = usePathname()
  const params = useSearchParams()

  const person = params.get("person") ?? ""
  const duty = params.get("duty") ?? ""

  /*
    The fields keep local state so they stay responsive under the fingers, and
    realign when the url changes without them, which is the Back button and the
    Clear button. React documents this adjustment during render; doing it in an
    effect would render once with the old value and again with the new one.
  */
  const [personText, setPersonText] = React.useState(person)
  const [seenPerson, setSeenPerson] = React.useState(person)
  if (person !== seenPerson) {
    setSeenPerson(person)
    setPersonText(person)
  }

  const [dutyText, setDutyText] = React.useState(duty)
  const [seenDuty, setSeenDuty] = React.useState(duty)
  if (duty !== seenDuty) {
    setSeenDuty(duty)
    setDutyText(duty)
  }

  const pending = React.useRef<ReturnType<typeof setTimeout> | null>(null)
  React.useEffect(
    () => () => {
      if (pending.current) clearTimeout(pending.current)
    },
    [],
  )

  const write = React.useCallback(
    (next: URLSearchParams, mode: "push" | "replace") => {
      const href = next.size ? `${pathname}?${next}` : pathname
      router[mode](href, { scroll: false })
    },
    [pathname, router],
  )

  const onType = (key: "person" | "duty", value: string) => {
    if (key === "person") setPersonText(value)
    else setDutyText(value)
    if (pending.current) clearTimeout(pending.current)
    pending.current = setTimeout(() => {
      const next = new URLSearchParams(params.toString())
      if (value) next.set(key, value)
      else next.delete(key)
      write(next, "replace")
    }, 250)
  }

  // Base UI hands null when a select is cleared, which this one cannot be: the
  // three orderings are exhaustive and there is no unsorted state to fall to.
  const onSort = (value: LateSort | null) => {
    if (!value) return
    const next = new URLSearchParams(params.toString())
    next.set("sort", value)
    write(next, "push")
  }

  const onGroup = () => {
    const next = new URLSearchParams(params.toString())
    // Grouped is the default and carries no parameter. Only the reader who has
    // turned it off is spending a word in the url on it.
    if (grouped) next.set("group", "off")
    else next.delete("group")
    write(next, "push")
  }

  const filtered = person !== "" || duty !== ""

  const onClear = () => {
    const next = new URLSearchParams(params.toString())
    next.delete("person")
    next.delete("duty")
    // Pushed, so the reader who cleared by mistake gets their filter back with
    // Back rather than by retyping a Thai name.
    write(next, "push")
  }

  return (
    <div className="flex flex-wrap items-center gap-3">
      <InputGroup className="h-10 w-full sm:w-64">
        <InputGroupAddon>
          <IconUser />
        </InputGroupAddon>
        <InputGroupInput
          list="team-people"
          placeholder={t("team.anyPerson")}
          aria-label={t("team.person")}
          value={personText}
          onChange={(e) => onType("person", e.target.value)}
        />
        {personText ? (
          <InputGroupAddon align="inline-end">
            <InputGroupButton
              aria-label={t("action.clear")}
              onClick={() => onType("person", "")}
            >
              <IconX />
            </InputGroupButton>
          </InputGroupAddon>
        ) : null}
      </InputGroup>
      <datalist id="team-people">
        {people.map((name) => (
          <option key={name} value={name} />
        ))}
      </datalist>

      <InputGroup className="h-10 w-full sm:w-72">
        <InputGroupAddon>
          <IconListCheck />
        </InputGroupAddon>
        <InputGroupInput
          list="team-duties"
          placeholder={t("team.anyDuty")}
          aria-label={t("team.duty")}
          value={dutyText}
          onChange={(e) => onType("duty", e.target.value)}
        />
        {dutyText ? (
          <InputGroupAddon align="inline-end">
            <InputGroupButton
              aria-label={t("action.clear")}
              onClick={() => onType("duty", "")}
            >
              <IconX />
            </InputGroupButton>
          </InputGroupAddon>
        ) : null}
      </InputGroup>
      <datalist id="team-duties">
        {duties.map((description) => (
          <option key={description} value={description} />
        ))}
      </datalist>

      {lateTab ? (
        <>
          <Select value={sort} onValueChange={onSort}>
            <SelectTrigger className="w-52" aria-label={t("team.sort")}>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              {LATE_SORTS.map((option) => (
                <SelectItem key={option} value={option}>
                  {t(SORT_LABEL[option])}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>

          <Button
            variant={grouped ? "default" : "outline"}
            onClick={onGroup}
            aria-pressed={grouped}
          >
            <IconLayersSubtract />
            {t("team.groupRepeats")}
          </Button>
        </>
      ) : null}

      <div className="ml-auto flex items-center gap-3">
        <p className="text-foreground/80 tabular-nums">{shown}</p>
        {/*
          Only when something is filtered. A Clear button on an unfiltered
          screen is a button that cannot do anything, and this row already
          carries four controls.
        */}
        {filtered ? (
          <Button variant="outline" onClick={onClear}>
            {t("team.clearFilters")}
          </Button>
        ) : null}
      </div>
    </div>
  )
}
