/**
 * Password security policy and account lockout helpers.
 *
 * These utilities are pure (no DB / I/O) so they can be safely used
 * in both server and client contexts.
 */

export const PASSWORD_RULES = {
  minLength: 8,
  requireUppercase: true,
  requireLowercase: true,
  requireNumber: true,
  requireSpecialChar: false,
  maxLoginAttempts: 5,
  lockoutDurationMinutes: 30,
} as const

export interface PasswordValidationResult {
  valid: boolean
  errors: string[]
}

/**
 * Validate a password against the configured policy.
 */
export function validatePassword(password: string): PasswordValidationResult {
  const errors: string[] = []

  if (password.length < PASSWORD_RULES.minLength) {
    errors.push(`Password must be at least ${PASSWORD_RULES.minLength} characters long`)
  }

  if (PASSWORD_RULES.requireUppercase && !/[A-Z]/.test(password)) {
    errors.push("Password must contain at least one uppercase letter")
  }

  if (PASSWORD_RULES.requireLowercase && !/[a-z]/.test(password)) {
    errors.push("Password must contain at least one lowercase letter")
  }

  if (PASSWORD_RULES.requireNumber && !/[0-9]/.test(password)) {
    errors.push("Password must contain at least one number")
  }

  if (PASSWORD_RULES.requireSpecialChar && !/[^A-Za-z0-9]/.test(password)) {
    errors.push("Password must contain at least one special character")
  }

  return { valid: errors.length === 0, errors }
}

export type PasswordStrength = "weak" | "medium" | "strong"

/**
 * Compute a coarse password strength label based on length and character
 * diversity. Useful for client-side strength indicators.
 */
export function getPasswordStrength(password: string): PasswordStrength {
  if (password.length === 0) return "weak"

  let score = 0
  if (password.length >= 8) score++
  if (password.length >= 12) score++
  if (/[a-z]/.test(password)) score++
  if (/[A-Z]/.test(password)) score++
  if (/[0-9]/.test(password)) score++
  if (/[^A-Za-z0-9]/.test(password)) score++

  if (score <= 2) return "weak"
  if (score <= 4) return "medium"
  return "strong"
}

/**
 * Determine whether an account should be considered locked given its
 * failed-attempt count and the timestamp of the most recent failure.
 *
 * The lockout is lifted automatically once `lockoutDurationMinutes` have
 * elapsed since `lastFailedAt`.
 */
export function isAccountLocked(
  failedAttempts: number,
  lastFailedAt: Date | null
): boolean {
  if (failedAttempts < PASSWORD_RULES.maxLoginAttempts) return false
  if (!lastFailedAt) return false

  const lockoutMs = PASSWORD_RULES.lockoutDurationMinutes * 60 * 1000
  const elapsed = Date.now() - lastFailedAt.getTime()
  return elapsed < lockoutMs
}

/**
 * Compute the remaining lockout time in milliseconds (0 if unlocked).
 */
export function getRemainingLockoutMs(
  failedAttempts: number,
  lastFailedAt: Date | null
): number {
  if (!isAccountLocked(failedAttempts, lastFailedAt)) return 0
  if (!lastFailedAt) return 0
  const lockoutMs = PASSWORD_RULES.lockoutDurationMinutes * 60 * 1000
  const elapsed = Date.now() - lastFailedAt.getTime()
  return Math.max(0, lockoutMs - elapsed)
}

/**
 * Human-readable summary of the password policy. Used by the UI to
 * display requirements next to the password input.
 */
export const PASSWORD_REQUIREMENT_HINTS: string[] = [
  `At least ${PASSWORD_RULES.minLength} characters`,
  PASSWORD_RULES.requireUppercase ? "At least one uppercase letter (A-Z)" : null,
  PASSWORD_RULES.requireLowercase ? "At least one lowercase letter (a-z)" : null,
  PASSWORD_RULES.requireNumber ? "At least one number (0-9)" : null,
  PASSWORD_RULES.requireSpecialChar
    ? "At least one special character (!@#$...)"
    : null,
].filter((s): s is string => Boolean(s))
