/**
 * `revokeOtherSessions: true` cuts the other terminals and not the caller's own.
 * Throwaway. `bun run tmp/settings/sessions.ts`.
 *
 * Two cookies for one person, standing for two machines on the factory floor.
 * The password is changed from the second and put straight back, so narin ends
 * the run on `admin1`.
 */

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

async function actionId(path: string, name: string, cookie?: 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]),
    ),
  ]
  const pattern = new RegExp(
    `__next_internal_action_entry_do_not_use__ \\[\\{"([0-9a-f]+)":\\{"name":"${name}"\\}\\}`,
  )
  for (const chunk of chunks) {
    const source = await fetch(`${BASE}${chunk}`).then((r) => r.text())
    const found = source.match(pattern)
    if (found) return found[1]!
  }
  throw new Error(`no action id for ${name}`)
}

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

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>
}

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

async function signIn(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(["narin", password]),
  })
  return { answer: answerIn(await r.text()), cookie: cookieOf(r) }
}

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

async function reaches(cookie: string) {
  const r = await fetch(`${BASE}/settings`, {
    headers: { cookie },
    redirect: "manual",
  })
  return r.status
}

const terminalA = await signIn("admin1")
const terminalB = await signIn("admin1")
show("terminal A reaches /settings", await reaches(terminalA.cookie))
show("terminal B reaches /settings", await reaches(terminalB.cookie))

const CHANGE = await actionId("/settings", "changePassword", terminalB.cookie)

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

const changed = await change(terminalB.cookie, "admin1", "cabbage5")
show("B changes the password", changed.answer)
show("B is still signed in", await reaches(changed.cookie))
show("A has been signed out", await reaches(terminalA.cookie))

const back = await change(changed.cookie, "cabbage5", "admin1")
show("B puts admin1 back", back.answer)
show("signing in with admin1", (await signIn("admin1")).answer)
show("signing in with cabbage5", (await signIn("cabbage5")).answer)
