import { describe, expect, it } from 'vitest'
import { issueCsrfToken, verifyDoubleSubmit, isSameOrigin } from './csrf'

describe('issueCsrfToken', () => {
  it('returns a non-empty opaque token', () => {
    const t = issueCsrfToken()
    expect(typeof t).toBe('string')
    expect(t.length).toBeGreaterThan(0)
  })

  it('returns a fresh token each call', () => {
    expect(issueCsrfToken()).not.toBe(issueCsrfToken())
  })

  it('token is base64url-safe (no + or / characters)', () => {
    for (let i = 0; i < 20; i++) {
      const t = issueCsrfToken()
      expect(t).not.toMatch(/[+/]/)
    }
  })

  it('token encodes at least 32 bytes of entropy (≥43 base64url chars)', () => {
    expect(issueCsrfToken().length).toBeGreaterThanOrEqual(43)
  })
})

describe('verifyDoubleSubmit', () => {
  it('accepts an equal cookie/submitted pair', () => {
    const t = issueCsrfToken()
    expect(verifyDoubleSubmit(t, t)).toBe(true)
  })

  it('rejects a mismatched pair', () => {
    expect(verifyDoubleSubmit(issueCsrfToken(), issueCsrfToken())).toBe(false)
  })

  it('fails closed on a missing side', () => {
    const t = issueCsrfToken()
    expect(verifyDoubleSubmit(null, t)).toBe(false)
    expect(verifyDoubleSubmit(t, null)).toBe(false)
    expect(verifyDoubleSubmit(undefined, t)).toBe(false)
    expect(verifyDoubleSubmit('', '')).toBe(false)
  })
})

describe('isSameOrigin', () => {
  const allowed = ['https://admin.example.com']

  it('accepts an Origin in the allow-list', () => {
    expect(isSameOrigin({ origin: 'https://admin.example.com' }, allowed)).toBe(true)
  })

  it('rejects an Origin not in the allow-list', () => {
    expect(isSameOrigin({ origin: 'https://evil.example.com' }, allowed)).toBe(false)
  })

  it('falls back to Sec-Fetch-Site when Origin is absent', () => {
    expect(isSameOrigin({ 'sec-fetch-site': 'same-origin' }, allowed)).toBe(true)
    expect(isSameOrigin({ 'sec-fetch-site': 'none' }, allowed)).toBe(true)
    expect(isSameOrigin({ 'sec-fetch-site': 'cross-site' }, allowed)).toBe(false)
  })

  it('rejects Sec-Fetch-Site: same-site (only same-origin and none are accepted)', () => {
    expect(isSameOrigin({ 'sec-fetch-site': 'same-site' }, allowed)).toBe(false)
  })

  it('fails closed when neither Origin nor Sec-Fetch-Site is present', () => {
    expect(isSameOrigin({}, allowed)).toBe(false)
  })

  it('accepts a Headers instance, not just a plain object', () => {
    const h = new Headers({ origin: 'https://admin.example.com' })
    expect(isSameOrigin(h, allowed)).toBe(true)
  })

  it('accepts one of several allowed origins', () => {
    const multi = ['https://app.example.com', 'https://admin.example.com']
    expect(isSameOrigin({ origin: 'https://app.example.com' }, multi)).toBe(true)
    expect(isSameOrigin({ origin: 'https://admin.example.com' }, multi)).toBe(true)
    expect(isSameOrigin({ origin: 'https://evil.example.com' }, multi)).toBe(false)
  })

  it('fails closed with an empty allow-list even for a well-formed Origin', () => {
    expect(isSameOrigin({ origin: 'https://admin.example.com' }, [])).toBe(false)
  })
})
