// ── Username policy ────────────────────────────────────────────────────────
//
// Pure helpers shared by public registration and SSO auto-provisioning:
//   - a reserved-name blocklist (staff-impersonating / system names),
//   - normalization (trim; case is preserved for display, uniqueness folds case
//     at the DB via the column's _ci collation), and
//   - deriving a valid username from an external SSO profile.
//
// No I/O — the DB UNIQUE index is the source of truth for collisions; these
// helpers only shape/validate candidate names and pick suffixes to retry with.

// Allowed characters in a stored username: letters, digits, dot, underscore,
// dash. Length 3–32 (matches the register validator + the column width).
const USERNAME_RE = /^[A-Za-z0-9_.-]{3,32}$/
const MIN_LEN = 3
const MAX_LEN = 32

// Names that must never belong to a self-registered account because they imply
// staff/system authority or are otherwise confusing. Compared case-insensitively.
const RESERVED_USERNAMES = new Set([
  'admin',
  'administrator',
  'root',
  'system',
  'staff',
  'mod',
  'moderator',
  'owner',
  'support',
  'help',
  'null',
  'undefined',
  'me',
  'anonymous',
  'everyone',
  'here',
])

// Trim surrounding whitespace. Case is preserved (stored as entered); the DB's
// _ci collation folds case for uniqueness + lookup.
function normalizeUsername(raw) {
  return typeof raw === 'string' ? raw.trim() : ''
}

function isReserved(name) {
  return RESERVED_USERNAMES.has(String(name || '').trim().toLowerCase())
}

function isValidFormat(name) {
  return USERNAME_RE.test(name)
}

// Validate a user-chosen username for registration. Returns { ok, message }.
function validateUsername(raw) {
  const name = normalizeUsername(raw)
  if (!isValidFormat(name)) {
    return { ok: false, message: 'Username must be 3–32 characters (letters, numbers, . _ -).' }
  }
  if (isReserved(name)) {
    return { ok: false, message: 'That username is not available.' }
  }
  return { ok: true, name }
}

// Reduce an arbitrary string to the allowed charset, clamped to MAX_LEN. Used as
// the base for SSO-derived usernames before uniqueness suffixing.
function sanitizeToUsername(raw) {
  let s = String(raw || '')
    .normalize('NFKD')
    .replace(/[^A-Za-z0-9_.-]/g, '')
    .replace(/^[._-]+/, '') // don't start with punctuation
    .slice(0, MAX_LEN)
  return s
}

// Derive a base username from a normalized SSO profile ({ name, email, subject }).
// Tries display name, then the email local-part, then a generic 'player' base.
// The result is always a valid *base* (>= MIN_LEN, sanitized) but is NOT
// guaranteed unique — the caller suffixes + retries against the UNIQUE index.
function deriveUsernameBase(profile) {
  const candidates = [profile && profile.name, profile && (profile.email || '').split('@')[0]]
  for (const c of candidates) {
    const s = sanitizeToUsername(c)
    if (s.length >= MIN_LEN && !isReserved(s)) return s
  }
  return 'player'
}

// Build the Nth candidate username for the dedup retry loop: attempt 0 is the
// bare base (padded if short), later attempts append an increasing numeric
// suffix, always clamped to MAX_LEN so the suffix survives truncation.
function candidateUsername(base, attempt) {
  const safeBase = base.length >= MIN_LEN ? base : `${base}player`.slice(0, MAX_LEN)
  if (attempt === 0) return safeBase
  const suffix = String(attempt + 1) // 2, 3, 4, …
  return `${safeBase.slice(0, MAX_LEN - suffix.length)}${suffix}`
}

module.exports = {
  USERNAME_RE,
  MIN_LEN,
  MAX_LEN,
  RESERVED_USERNAMES,
  normalizeUsername,
  isReserved,
  isValidFormat,
  validateUsername,
  sanitizeToUsername,
  deriveUsernameBase,
  candidateUsername,
}
