import { describe, expect, test } from "bun:test"

import {
  DEMO_WORK,
  dutiesFor,
  inRankOrder,
  type Chair,
} from "@/lib/demo-duties"

const chair = (n: number, level: Chair["level"]): Chair => ({
  positionId: `p${n}`,
  title: `Title ${n}`,
  level,
})

const line: Chair[] = [
  chair(1, "staff"),
  chair(2, "line_leader"),
  chair(3, "staff"),
  chair(4, "supervisor"),
]

describe("inRankOrder", () => {
  test("puts the head of the department first, so `at: 0` means the head", () => {
    expect(inRankOrder(line).map((c) => c.positionId)).toEqual([
      "p4",
      "p2",
      "p1",
      "p3",
    ])
  })

  test("does not disturb the caller's array", () => {
    const copy = [...line]
    inRankOrder(line)
    expect(line).toEqual(copy)
  })
})

describe("dutiesFor", () => {
  test("a department with nobody in it carries nothing", () => {
    expect(dutiesFor(DEMO_WORK.Accounting!, [])).toEqual([])
  })

  test("`everyone` gives each holder their own copy", () => {
    const duties = dutiesFor(
      [{ text: "Keep the bench clean", occurrence: "general", everyone: true }],
      line,
    )
    expect(duties).toHaveLength(4)
    expect(new Set(duties.map((d) => d.positionId)).size).toBe(4)
  })

  test("`rotate` deals one of each occurrence to every holder", () => {
    const duties = dutiesFor(
      [
        { text: "A", occurrence: "weekly", dayOfWeek: 1, rotate: true },
        { text: "B", occurrence: "weekly", dayOfWeek: 3, rotate: true },
        { text: "C", occurrence: "monthly", dayOfMonth: 5, rotate: true },
      ],
      line,
    )
    for (const c of line) {
      const mine = duties.filter((d) => d.positionId === c.positionId)
      expect(mine.filter((d) => d.occurrence === "weekly")).toHaveLength(1)
      expect(mine.filter((d) => d.occurrence === "monthly")).toHaveLength(1)
    }
    expect(new Set(duties.map((d) => d.description))).toEqual(
      new Set(["A", "B", "C"]),
    )
  })

  test("`at` puts a daily check on one chair and nowhere else", () => {
    const duties = dutiesFor(
      [{ text: "Check the machine", occurrence: "daily", at: 1 }],
      line,
    )
    // Second from the top: the line leader, not the supervisor.
    expect(duties).toEqual([
      {
        positionId: "p2",
        description: "Check the machine",
        occurrence: "daily",
        dayOfWeek: null,
        dayOfMonth: null,
        auditedBy: null,
      },
    ])
  })

  test("a chair the department does not have drops the duty rather than moving it", () => {
    expect(
      dutiesFor([{ text: "Sign it off", occurrence: "daily", at: 9 }], line),
    ).toEqual([])
  })

  test("the schedule matches the occurrence, which the database also checks", () => {
    for (const shapes of Object.values(DEMO_WORK)) {
      for (const duty of dutiesFor(shapes, line)) {
        if (duty.occurrence === "weekly") {
          expect(duty.dayOfWeek).not.toBeNull()
          expect(duty.dayOfMonth).toBeNull()
        } else if (
          duty.occurrence === "monthly" ||
          duty.occurrence === "quarterly"
        ) {
          expect(duty.dayOfMonth).not.toBeNull()
          expect(duty.dayOfWeek).toBeNull()
        } else {
          expect(duty.dayOfWeek).toBeNull()
          expect(duty.dayOfMonth).toBeNull()
        }
      }
    }
  })
})

describe("the catalogue", () => {
  test("covers 23 departments, half of the 46 in the roster", () => {
    expect(Object.keys(DEMO_WORK)).toHaveLength(23)
  })

  test("every department gives everybody in it a job description line", () => {
    for (const [name, shapes] of Object.entries(DEMO_WORK)) {
      expect(
        shapes.some((s) => s.occurrence === "general" && s.everyone),
        `${name} has no general duty for everybody`,
      ).toBe(true)
    }
  })

  test("every department gives everybody in it work that generates", () => {
    for (const [name, shapes] of Object.entries(DEMO_WORK)) {
      const duties = dutiesFor(shapes, line)
      for (const c of line) {
        const generating = duties.filter(
          (d) => d.positionId === c.positionId && d.occurrence !== "general",
        )
        expect(
          generating.length,
          `${name} leaves ${c.title} with nothing`,
        ).toBeGreaterThan(0)
      }
    }
  })

  test("no weekly duty falls on a Sunday, which is nobody's working day", () => {
    for (const shapes of Object.values(DEMO_WORK)) {
      for (const shape of shapes) {
        if (shape.occurrence === "weekly") {
          expect(shape.dayOfWeek).toBeGreaterThanOrEqual(1)
          expect(shape.dayOfWeek).toBeLessThanOrEqual(5)
        }
      }
    }
  })

  test("no monthly duty asks for a day short months do not have", () => {
    for (const shapes of Object.values(DEMO_WORK)) {
      for (const shape of shapes) {
        if (
          shape.occurrence === "monthly" ||
          shape.occurrence === "quarterly"
        ) {
          expect(shape.dayOfMonth).toBeGreaterThanOrEqual(1)
          expect(shape.dayOfMonth).toBeLessThanOrEqual(28)
        }
      }
    }
  })

  test("somebody outside the pilot is somebody with an empty plate", () => {
    // The two departments docs/test-accounts.csv keeps empty on purpose.
    expect(DEMO_WORK["Human Resources and Administration"]).toBeUndefined()
    expect(DEMO_WORK["Asset Security"]).toBeUndefined()
  })

  test("at least one duty names a verifier, which none did before", () => {
    const audited = Object.values(DEMO_WORK)
      .flat()
      .filter((s) => s.auditedBy)
    expect(audited.length).toBeGreaterThan(0)
  })

  test("nothing is written in a language the product does not speak", () => {
    // The previous demonstration left three duties in French behind. Latin
    // letters, digits and ordinary punctuation only.
    for (const shapes of Object.values(DEMO_WORK)) {
      for (const shape of shapes) {
        expect(shape.text).toMatch(/^[\x20-\x7E]+$/)
      }
    }
  })
})
