import { describe, expect, it } from 'vitest'

import {
  PASSWORD_HINT,
  PASSWORD_MIN_LENGTH,
  passwordChecks,
} from '../passwordPolicy'

/**
 * The client's statement of the password policy.
 *
 * The server is the authority, so what matters here is that the two agree: a
 * form that promises less than the server enforces produces a rejection the
 * person can see no cause for, and one that promises more turns valid passwords
 * away before they are ever sent.
 *
 * Keep in step with `AppServiceProvider::configurePasswords()`.
 */
describe('passwordPolicy', () => {
  it('states the same floor the server enforces', () => {
    expect(PASSWORD_MIN_LENGTH).toBe(6)
  })

  it('builds the hint from the floor rather than repeating it', () => {
    expect(PASSWORD_HINT).toContain(String(PASSWORD_MIN_LENGTH))
  })

  it('marks every rule met for a password at the floor', () => {
    const checks = passwordChecks('abc123')

    expect(checks.every((check) => check.met)).toBe(true)
  })

  it('fails the length rule one character under the floor', () => {
    const checks = passwordChecks('abc12')

    expect(checks[0]?.met).toBe(false)
    // The character-class rules are independent and still satisfied.
    expect(checks[1]?.met).toBe(true)
    expect(checks[2]?.met).toBe(true)
  })

  /** A six-digit PIN clears the length on its own; the letter rule is what stops it. */
  it('fails the letter rule for digits alone', () => {
    const checks = passwordChecks('123456')

    expect(checks[0]?.met).toBe(true)
    expect(checks[1]?.met).toBe(false)
  })

  it('fails the number rule for letters alone', () => {
    const checks = passwordChecks('abcdef')

    expect(checks[0]?.met).toBe(true)
    expect(checks[2]?.met).toBe(false)
  })

  /**
   * The letter test is Unicode-aware on purpose. This marketplace serves a
   * Nepali audience, and `\w` would refuse a Devanagari passphrase outright
   * while the server -- which uses the same rule -- accepted it.
   */
  it('counts non-latin script as letters', () => {
    const checks = passwordChecks('पासवर्ड1')

    expect(checks[1]?.met).toBe(true)
    expect(checks[2]?.met).toBe(true)
  })

  it('reports no rule as met for an empty password', () => {
    expect(passwordChecks('').some((check) => check.met)).toBe(false)
  })
})
