import { timingSafeEqual } from '@platform-modules/util/crypto'
import {
  hashPassword as platformHashPassword,
  verifyPassword as platformVerifyPassword,
  type DeriveBitsFn,
} from '@platform-modules/util/password'

import { pbkdf2Derive } from '../../../../../packages/auth/src/password'

const LEGACY_HASH = 'SHA-256'
const LEGACY_KEY_BYTES = 32

export type PlatformPasswordVerification = {
  ok: boolean
  needsRehash: boolean
  rehashHash?: string
}

function toB64(bytes: Uint8Array | ArrayBuffer): string {
  const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
  return btoa(String.fromCharCode(...arr))
}

function fromB64(b64: string): Uint8Array {
  return new Uint8Array(atob(b64).split('').map((c) => c.charCodeAt(0)))
}

export function isLegacyPasswordHash(storedHash: string): boolean {
  return storedHash.startsWith('pbkdf2$')
}

async function verifyLegacyPasswordHash(
  plain: string,
  storedHash: string,
  deriveBits: DeriveBitsFn,
): Promise<boolean> {
  const parts = storedHash.split('$')
  if (parts.length !== 4) return false

  const [, iterStr, saltB64, hashB64] = parts
  const iterations = Number(iterStr)
  if (!Number.isInteger(iterations) || iterations <= 0 || iterations > 100_000) return false

  let salt: Uint8Array
  try {
    salt = fromB64(saltB64!)
  } catch {
    return false
  }

  const bits = await deriveBits(plain, salt, iterations, LEGACY_HASH, LEGACY_KEY_BYTES)
  return timingSafeEqual(toB64(bits), hashB64!)
}

export async function hashPlatformPassword(
  plain: string,
  opts?: { deriveBits?: DeriveBitsFn },
): Promise<string> {
  return platformHashPassword(plain, opts)
}

export async function verifyPlatformPasswordHash(
  plain: string,
  storedHash: string,
  opts?: { deriveBits?: DeriveBitsFn },
): Promise<PlatformPasswordVerification> {
  const deriveBits = opts?.deriveBits ?? pbkdf2Derive

  if (isLegacyPasswordHash(storedHash)) {
    const ok = await verifyLegacyPasswordHash(plain, storedHash, deriveBits)
    if (!ok) return { ok: false, needsRehash: false }

    return {
      ok: true,
      needsRehash: true,
      rehashHash: await platformHashPassword(plain, opts),
    }
  }

  const ok = await platformVerifyPassword(plain, storedHash, opts)
  return { ok, needsRehash: false }
}
