"use client"

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

import { BrandMark } from "@/components/brand-mark"
import { useT } from "@/components/locale-provider"
import { NavMain, type NavItem } from "@/components/nav-main"
import { LanguageSwitch } from "@/components/language-switch"
import { NavUser } from "@/components/nav-user"
import { ThemeSwitch } from "@/components/theme-switch"
import type { Translate } from "@/lib/i18n"
import { activeUrl } from "@/lib/nav-active"
import type { SignedIn } from "@/lib/services/session"
import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarHeader,
  SidebarRail,
} from "@/components/ui/sidebar"
import {
  IconBell,
  IconBellRinging,
  IconBuildingFactory2,
  IconCalendarEvent,
  IconChartBar,
  IconChecks,
  IconClipboardPlus,
  IconFileDescription,
  IconFileStack,
  IconLayoutDashboard,
  IconListCheck,
  IconRepeat,
  IconUsers,
} from "@tabler/icons-react"

/**
 * Three tiers, from the functional specification §3 and §4. They nest: a
 * manager sees their own work plus their department, an admin sees both plus
 * the structure tables that only they may touch.
 *
 * A `#` is a screen that does not exist yet, and the entry stays so the shape
 * of the finished thing is visible. Every other url is real.
 */
export type Tier = "staff" | "manager" | "admin"

/**
 * A function rather than a constant, because one of its entries carries a
 * number that belongs to the person looking. The count is read once per request
 * by the shell and handed down; nothing here asks a database.
 */
const ownWork = (unread: number, tier: Tier): NavItem[] => [
  {
    title: "nav.notifications",
    url: "/notifications",
    icon: <IconBell />,
    unread,
  },
  { title: "nav.myTasks", url: "/tasks", icon: <IconListCheck /> },
  {
    title: "nav.myJobDescription",
    url: "/job-description",
    icon: <IconFileDescription />,
  },
  // Under My work and not My department: being somebody's checker is a fact
  // about the rows, not about a tier, and three of the seven checkers in the
  // roster are staff.
  { title: "review.title", url: "/review", icon: <IconChecks /> },
  /*
    Two entries only staff see, and each is a screen somebody else reaches by
    another door.

    The first is `/tasks/new`, which the manager tier reaches under My
    department as Create and assign. A staff member does not assign: their
    scope is the one position they hold, so the entry is named for what they
    will actually get. It is drawn for them only, because a manager already has
    the url under the other heading and the same link twice in one sidebar is a
    sidebar that reads as broken.

    The second is KPI, which is one screen appearing once, in the group that
    describes what the reader will find on it. For staff that is their own four
    figures, which is their own work and belongs here; for everybody else it is
    a department, and the entry moves down to My department below.

    KPI used to sit under Administration pointing at `#`. That was wrong twice
    over: the screen is a read and not a structure table, and putting the only
    way to it behind the admin tier is the defect ELEVA-116 names, a person
    evaluated on figures they cannot see.
  */
  ...(tier === "staff"
    ? ([
        {
          title: "selfDuties.title",
          url: "/tasks/new",
          icon: <IconClipboardPlus />,
        },
        { title: "nav.kpi", url: "/kpi", icon: <IconChartBar /> },
      ] satisfies NavItem[])
    : []),
]

const DEPARTMENT: NavItem[] = [
  { title: "nav.teamOverview", url: "/team", icon: <IconLayoutDashboard /> },
  {
    title: "nav.createAndAssign",
    url: "/tasks/new",
    icon: <IconClipboardPlus />,
  },
  // Beside the team overview, because it is the same department read over a
  // month instead of a morning, and it takes the same `?dept=` filter.
  { title: "nav.kpi", url: "/kpi", icon: <IconChartBar /> },
  { title: "nav.reminders", url: "#", icon: <IconBellRinging /> },
]

const ADMINISTRATION: NavItem[] = [
  {
    title: "nav.departmentsAndPositions",
    url: "/positions",
    icon: <IconBuildingFactory2 />,
  },
  {
    title: "nav.peopleAndAssignments",
    url: "/admin/people",
    icon: <IconUsers />,
  },
  /*
    Beside People & assignments rather than under Departments & positions,
    because the question it answers is about people: who is sitting in a job
    nothing describes yet. The description itself is written on the position,
    and every row here links there.
  */
  {
    title: "nav.jobDescriptions",
    url: "/admin/job-descriptions",
    icon: <IconFileStack />,
  },
  {
    title: "nav.holidayCalendar",
    url: "/admin/holidays",
    icon: <IconCalendarEvent />,
  },
  /*
    Read only, and it writes nothing. It is here rather than left at a url
    somebody has to know about, because its whole purpose is to let the
    recurrence rules be argued with by people who will never open
    `lib/recurrence.ts`.
  */
  { title: "recurrence.title", url: "/admin/recurrence", icon: <IconRepeat /> },
]

/**
 * A job title and an access level are not the same thing, and this footer is
 * where they get confused. §3 folds four titles (Manager, Assistant Manager,
 * Supervisor, Line Leader) into one "manager level", while an admin holds a
 * position like anybody else. So the line reads position · scope, and the tier
 * is never a word buried inside a job title.
 *
 * An admin works across every department, so their scope is not a department
 * name. Somebody between positions has no title to show, and saying so is
 * better than an empty line that reads as a rendering fault.
 *
 * The position title and the department name are data and stay as the roster
 * spells them. The three words around them are interface, and this line sits
 * directly above the language switch, which makes it the one line a Thai reader
 * is certain to be looking at while changing the language.
 */
/**
 * The one entry that gets the highlight, decided across all three groups at
 * once rather than group by group.
 *
 * It has to be all of them together: `/tasks` sits under My work and
 * `/tasks/new` under My department, so a group that only knew its own entries
 * would light both. `activeUrl` takes the longest match and there is exactly
 * one winner, which is what the highlight is supposed to mean.
 *
 * This was hard-coded `isActive: true` on My tasks until 5 August. The entry
 * was therefore lit on every screen in the application, which makes the
 * highlight worse than absent: it says "you are here" and is wrong everywhere
 * but one page.
 */
function markActive(items: NavItem[], active: string | null): NavItem[] {
  return items.map((item) => ({ ...item, isActive: item.url === active }))
}

function describe(user: SignedIn, t: Translate): string {
  const scope =
    user.tier === "admin"
      ? t("nav.allDepartments")
      : (user.department ?? t("nav.noDepartment"))
  return `${user.positionTitle ?? t("nav.noPosition")} · ${scope}`
}

export function AppSidebar({
  user,
  unread,
  ...props
}: React.ComponentProps<typeof Sidebar> & {
  user: SignedIn
  /** How many notifications this person has not seen. A number, so this stays a client component with nothing under it. */
  unread: number
}) {
  const tier = user.tier
  const t = useT()
  const pathname = usePathname()

  const own = ownWork(unread, tier)
  /*
    Every url the sidebar can draw for this person, not only the group being
    rendered, because the longest match has to be chosen against all of them at
    once. The groups a tier does not see are included: they carry no url this
    person can be on, so they change nothing, and leaving them out would make
    the answer depend on the tier for no reason.
  */
  const active = activeUrl(pathname, [
    ...own.map((item) => item.url),
    ...DEPARTMENT.map((item) => item.url),
    ...ADMINISTRATION.map((item) => item.url),
  ])

  return (
    <Sidebar collapsible="icon" {...props}>
      <SidebarHeader>
        <BrandMark />
      </SidebarHeader>
      <SidebarContent>
        <NavMain label="nav.myWork" items={markActive(own, active)} />
        {tier !== "staff" ? (
          <NavMain
            label="nav.myDepartment"
            items={markActive(DEPARTMENT, active)}
          />
        ) : null}
        {tier === "admin" ? (
          <NavMain
            label="nav.administration"
            items={markActive(ADMINISTRATION, active)}
          />
        ) : null}
      </SidebarContent>
      <SidebarFooter>
        {/*
          Above the person and not inside their menu. Somebody who cannot read
          the interface should not have to open a menu labelled in the language
          they cannot read in order to change it, and somebody who cannot see
          the screen should not have to find a menu they cannot see. The two
          controls answer the same kind of problem, so they sit together.

          They are stored in opposite places, deliberately, and each file says
          why: the language is a fact about the person, the theme is a fact
          about the room.
        */}
        <LanguageSwitch />
        <ThemeSwitch />
        <NavUser
          user={{ name: user.name, email: describe(user, t), avatar: "" }}
        />
      </SidebarFooter>
      <SidebarRail />
    </Sidebar>
  )
}
