import { describe, it, expect } from 'vitest'
import {
  hashToken,
  generateToken,
  validateAdminSecret,
  getClientIp,
  createJwt,
  verifyJwt,
} from '@/lib/auth'

describe('auth: token utilities', () => {
  it('generateToken produces a 96-character hex string (48 random bytes)', () => {
    const t = generateToken()
    expect(t).toMatch(/^[0-9a-f]{96}$/)
  })

  it('generateToken produces unique values across calls', () => {
    const seen = new Set()
    for (let i = 0; i < 100; i++) seen.add(generateToken())
    expect(seen.size).toBe(100)
  })

  it('hashToken is deterministic for the same input', () => {
    const a = hashToken('input')
    const b = hashToken('input')
    expect(a).toBe(b)
  })

  it('hashToken produces a 64-char hex SHA-256 digest', () => {
    expect(hashToken('anything')).toMatch(/^[0-9a-f]{64}$/)
  })

  it('hashToken produces different output for different input', () => {
    expect(hashToken('a')).not.toBe(hashToken('b'))
  })
})

describe('auth: validateAdminSecret (CLI bearer fallback)', () => {
  it('accepts the correct bearer header', () => {
    const req = new Request('http://x', {
      headers: { authorization: `Bearer ${process.env.PITCH_ADMIN_SECRET}` },
    })
    expect(validateAdminSecret(req)).toBe(true)
  })

  it('rejects a wrong bearer secret', () => {
    const req = new Request('http://x', {
      headers: { authorization: 'Bearer wrong-secret' },
    })
    expect(validateAdminSecret(req)).toBe(false)
  })

  it('rejects requests with no Authorization header', () => {
    const req = new Request('http://x')
    expect(validateAdminSecret(req)).toBe(false)
  })

  it('rejects bare secret without Bearer prefix', () => {
    const req = new Request('http://x', {
      headers: { authorization: process.env.PITCH_ADMIN_SECRET || '' },
    })
    expect(validateAdminSecret(req)).toBe(false)
  })
})

describe('auth: getClientIp', () => {
  it('parses x-forwarded-for', () => {
    const req = new Request('http://x', {
      headers: { 'x-forwarded-for': '10.0.0.1' },
    })
    expect(getClientIp(req)).toBe('10.0.0.1')
  })

  it('takes the first hop from a comma-separated x-forwarded-for', () => {
    const req = new Request('http://x', {
      headers: { 'x-forwarded-for': '10.0.0.1, 192.168.1.1, 172.16.0.1' },
    })
    expect(getClientIp(req)).toBe('10.0.0.1')
  })

  it('trims whitespace around the first IP', () => {
    const req = new Request('http://x', {
      headers: { 'x-forwarded-for': '  10.0.0.1  , 192.168.1.1' },
    })
    expect(getClientIp(req)).toBe('10.0.0.1')
  })

  it('returns null when the header is absent', () => {
    const req = new Request('http://x')
    expect(getClientIp(req)).toBeNull()
  })
})

describe('auth: investor JWT roundtrip', () => {
  const payload = {
    sub: 'investor-id',
    email: 'investor@example.com',
    sessionId: 'session-id',
  }

  it('createJwt + verifyJwt roundtrip preserves payload', async () => {
    const jwt = await createJwt(payload)
    const decoded = await verifyJwt(jwt)
    expect(decoded?.sub).toBe(payload.sub)
    expect(decoded?.email).toBe(payload.email)
    expect(decoded?.sessionId).toBe(payload.sessionId)
  })

  it('verifyJwt rejects garbage', async () => {
    expect(await verifyJwt('not-a-jwt')).toBeNull()
  })

  it('verifyJwt rejects a tampered signature', async () => {
    const jwt = await createJwt(payload)
    const tampered = jwt.slice(0, -2) + 'XX'
    expect(await verifyJwt(tampered)).toBeNull()
  })
})
