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

import {
  hashPassword as moduleHashPassword,
  verifyPassword as moduleVerifyPassword,
} from '@platform-modules/util/password'
import {
  hashPassword as hostHashPassword,
  verifyPassword as hostVerifyPassword,
} from '../../../../../../packages/auth/src/password'

import { verifyPlatformPasswordHash } from '../auth-hash'

async function deriveLegacySha256Hash(plain: string, salt: Uint8Array, iterations: number): Promise<string> {
  const keyMaterial = await crypto.subtle.importKey(
    'raw',
    new TextEncoder().encode(plain),
    'PBKDF2',
    false,
    ['deriveBits'],
  )
  const bits = await crypto.subtle.deriveBits(
    { name: 'PBKDF2', salt, iterations, hash: 'SHA-256' },
    keyMaterial,
    32 * 8,
  )
  const arr = new Uint8Array(bits)
  const saltB64 = btoa(String.fromCharCode(...salt))
  const hashB64 = btoa(String.fromCharCode(...arr))
  return `pbkdf2$${iterations}$${saltB64}$${hashB64}`
}

describe('auth-hash platform parity', () => {
  it('verifies an existing zync pbkdf2sha512 hash via the platform engine', async () => {
    const stored = await hostHashPassword('correct horse battery staple')

    await expect(moduleVerifyPassword('correct horse battery staple', stored)).resolves.toBe(true)
    await expect(moduleVerifyPassword('wrong password', stored)).resolves.toBe(false)

    await expect(verifyPlatformPasswordHash('correct horse battery staple', stored)).resolves.toMatchObject({
      ok: true,
      needsRehash: false,
    })
  })

  it('cross-verifies a module-derived hash with the host verifier', async () => {
    const stored = await moduleHashPassword('module-native password')

    await expect(moduleVerifyPassword('module-native password', stored)).resolves.toBe(true)
    await expect(hostVerifyPassword('module-native password', stored)).resolves.toBe(true)
    await expect(verifyPlatformPasswordHash('module-native password', stored)).resolves.toMatchObject({
      ok: true,
      needsRehash: false,
    })
  })

  it('keeps legacy sha256 verification and emits a rehash target', async () => {
    const salt = new Uint8Array(Array.from({ length: 16 }, (_, index) => index + 1))
    const stored = await deriveLegacySha256Hash('legacy-password', salt, 10_000)

    const result = await verifyPlatformPasswordHash('legacy-password', stored)

    expect(result.ok).toBe(true)
    expect(result.needsRehash).toBe(true)
    expect(typeof result.rehashHash).toBe('string')
    await expect(moduleVerifyPassword('legacy-password', result.rehashHash!)).resolves.toBe(true)
    await expect(hostVerifyPassword('legacy-password', stored)).resolves.toBe(true)
    await expect(hostVerifyPassword('legacy-password', result.rehashHash!)).resolves.toBe(true)
  })
})
