/**
 * The other direction of the split counter, and what the screen actually says.
 * Throwaway. `bun run tmp/settings/cross.ts`.
 *
 * A run of failed sign-ins must not lock somebody out of the one screen that
 * fixes the password they cannot remember. The cookie is taken first, because
 * after the eleventh failure there is no signing in to take one with.
 */

const BASE = "http://127.0.0.1:4214"

async function sources(path: string, cookie?: string): Promise<string[]> {
  const html = await fetch(`${BASE}${path}`, {
    headers: cookie ? { cookie } : {},
  }).then((r) => r.text())
  const chunks = [
    ...new Set(
      [...html.matchAll(/\/_next\/static\/[^"'\\ ]+?\.js/g)].map((m) => m[0]),
    ),
  ]
  return Promise.all(
    chunks.map((c) => fetch(`${BASE}${c}`).then((r) => r.text())),
  )
}

async function actionId(path: string, name: string, cookie?: string) {
  const pattern = new RegExp(
    `__next_internal_action_entry_do_not_use__ \\[\\{"([0-9a-f]+)":\\{"name":"${name}"\\}\\}`,
  )
  for (const source of await sources(path, cookie)) {
    const found = source.match(pattern)
    if (found) return found[1]!
  }
  throw new Error(`no action id for ${name}`)
}

function answerIn(body: string) {
  const line = body.match(/\{"ok":(?:true|false)[^}]*\}/)
  if (!line) throw new Error(`no answer in: ${body.slice(0, 400)}`)
  return JSON.parse(line[0]) as Record<string, unknown>
}

function cookieOf(r: Response) {
  return r.headers
    .getSetCookie()
    .map((l) => l.split(";")[0]!)
    .join("; ")
}

const SIGN_IN = await actionId("/login", "attemptSignIn")

async function signIn(username: string, password: string) {
  const r = await fetch(`${BASE}/login`, {
    method: "POST",
    headers: {
      "Next-Action": SIGN_IN,
      "Content-Type": "text/plain;charset=UTF-8",
    },
    body: JSON.stringify([username, password]),
  })
  return { answer: answerIn(await r.text()), cookie: cookieOf(r) }
}

function show(label: string, value: unknown) {
  console.log(`  ${label.padEnd(46)}${JSON.stringify(value)}`)
}

const who = "malai"
const { cookie } = await signIn(who, "manager1")

console.log("--- what /settings renders for a signed-in manager ---")
const page = await fetch(`${BASE}/settings`, { headers: { cookie } }).then((r) =>
  r.text(),
)
for (const needle of [
  "Settings",
  "Current password",
  "New password again",
  "At least 6 characters.",
  "Change password",
  "Language",
  "English",
  "ไทย",
]) {
  show(`renders "${needle}"`, page.includes(needle))
}

console.log("\n--- eleven failed sign-ins, then the settings screen ---")
let last: unknown
for (let n = 1; n <= 11; n++) {
  const { answer } = await signIn(who, "definitely-not-it")
  last = answer
}
show("eleventh sign-in", last)

const CHANGE = await actionId("/settings", "changePassword", cookie)
const r = await fetch(`${BASE}/settings`, {
  method: "POST",
  headers: {
    "Next-Action": CHANGE,
    "Content-Type": "text/plain;charset=UTF-8",
    cookie,
  },
  // Wrong on purpose, so nothing is changed: what is read is whether the answer
  // is "wrong password" rather than "too many attempts".
  body: JSON.stringify(["not-it", "cabbage4", "cabbage4"]),
})
show("settings screen while sign-in is blocked", answerIn(await r.text()))
