import { positionLevel } from "@/lib/db/schema"

/**
 * Reading the client's roster export, with no database and no file system in
 * sight.
 *
 * Separate from the seed because what matters here is not the happy path, which
 * the real export exercises on every run, but what happens to next year's
 * export when a column has been renamed or a new rank invented. It should stop
 * with a sentence naming the line, not import 190 rows and drop three.
 */

export type Level = (typeof positionLevel.enumValues)[number]

/** The client writes these six labels. Anything else is a data problem. */
export const LEVELS: Record<string, Level> = {
  Admin: "admin",
  Manager: "manager",
  "Assistant Manager": "assistant_manager",
  Supervisor: "supervisor",
  "Line Leader": "line_leader",
  Staff: "staff",
}

export type RosterRow = {
  department: string
  position: string
  code: string
  firstName: string
  lastName: string
  level: Level
}

const COLUMNS = [
  "Departments",
  "Positions",
  "Employee Code",
  "Name",
  "Last Name",
  "Level",
] as const

export function parseRoster(text: string): RosterRow[] {
  const lines = text
    // The spreadsheet export carries a byte order mark, which would otherwise
    // become part of the first column's name and hide it.
    .replace(/^﻿/, "")
    .split("\n")
    .filter((line) => line.trim() !== "")

  if (lines.length === 0) throw new Error("The export is empty.")

  // Headers are trimmed: the real file's fourth column is "Name " with a
  // trailing space, and that is not worth failing over.
  const header = lines[0]!.split("\t").map((h) => h.trim())
  const at = COLUMNS.map((column) => {
    const index = header.indexOf(column)
    if (index === -1) {
      throw new Error(
        `The export has no "${column}" column. It has: ${header.join(", ")}`,
      )
    }
    return index
  })

  return lines.slice(1).map((line, i) => {
    const cells = line.split("\t").map((c) => c.trim())
    const [department, position, code, firstName, lastName, label] = at.map(
      (index) => cells[index] ?? "",
    )
    const at_ = `Line ${i + 2}`

    const level = LEVELS[label!]
    if (!level) {
      throw new Error(
        `${at_}: "${label}" is not a level. Expected one of ${Object.keys(LEVELS).join(", ")}.`,
      )
    }
    // The code is what people log in with, so a malformed one is worth stopping
    // for rather than storing and discovering at a terminal.
    if (!/^\d{8}$/.test(code!)) {
      throw new Error(`${at_}: "${code}" is not an eight digit employee code.`)
    }
    // The last name is deliberately not required. One person in the roster,
    // VAN QUANG TRAN, has their whole name in the Name column and nothing in
    // Last Name, which is a name written the way its owner writes it and not a
    // gap to reject. A missing given name is different: that is a person with
    // no name at all, and nothing downstream could display them.
    for (const [name, value] of [
      ["department", department],
      ["position", position],
      ["name", firstName],
    ] as const) {
      if (!value) throw new Error(`${at_}: the ${name} is empty.`)
    }

    return {
      department: department!,
      position: position!,
      code: code!,
      firstName: firstName!,
      lastName: lastName!,
      level,
    }
  })
}
