/**
 * B7 · unit tests for `buildUpgradeRequest` — the browser upgrade → P1 `UpgradeRequest` builder.
 * Focus: the DPoP proof transport (FUNCTIONAL BLOCKER). A browser cannot set the `dpop` request
 * header, so the proof rides the `term.dpop.<b64u>` subprotocol entry; the builder must read it there
 * while keeping the header path working (header WINS if both). Also pins `activeSessionCount` plumbing.
 */
import { describe, it, expect } from 'vitest'
import type { IncomingMessage } from 'node:http'
import { encodeBase64UrlString, APP_SUBPROTOCOL } from 'relay-contracts'
import { buildUpgradeRequest } from '../src/servers/browser-server.js'
import { DPOP_SUBPROTOCOL_PREFIX } from '../src/servers/dpop-subprotocol.js'

const PROOF = 'eyJhbGciOiJFZERTQSJ9.eyJodHUiOiJodHRwczovL2FsaWNlL3dzIn0.c2ln'

function dpopEntry(proofJws: string): string {
  return DPOP_SUBPROTOCOL_PREFIX + encodeBase64UrlString(proofJws)
}

function mkReq(headers: Record<string, string | undefined>): IncomingMessage {
  return {
    headers: { host: 'alice.example.com', ...headers },
    url: '/ws',
    socket: { remoteAddress: '1.2.3.4' },
  } as unknown as IncomingMessage
}

describe('buildUpgradeRequest — DPoP transport', () => {
  it('reads the proof from the term.dpop.<b64u> subprotocol when no header is set (the browser path)', () => {
    const req = mkReq({ 'sec-websocket-protocol': `${APP_SUBPROTOCOL}, ${dpopEntry(PROOF)}` })
    const upgrade = buildUpgradeRequest(req, 0)
    expect(upgrade.dpop.proof).toBe(PROOF)
  })

  it('prefers the dpop HEADER over the subprotocol entry when both are present (header wins)', () => {
    const req = mkReq({
      dpop: 'header-proof-jws',
      'sec-websocket-protocol': `${APP_SUBPROTOCOL}, ${dpopEntry(PROOF)}`,
    })
    expect(buildUpgradeRequest(req, 0).dpop.proof).toBe('header-proof-jws')
  })

  it('still honours the dpop header alone (header path unchanged)', () => {
    const req = mkReq({ dpop: 'header-only', 'sec-websocket-protocol': APP_SUBPROTOCOL })
    expect(buildUpgradeRequest(req, 0).dpop.proof).toBe('header-only')
  })

  it('yields proof=null when neither header nor subprotocol carries a proof', () => {
    const req = mkReq({ 'sec-websocket-protocol': APP_SUBPROTOCOL })
    expect(buildUpgradeRequest(req, 0).dpop.proof).toBeNull()
  })

  it('yields proof=null (fail-closed) for a malformed term.dpop. subprotocol entry', () => {
    const req = mkReq({ 'sec-websocket-protocol': `${APP_SUBPROTOCOL}, ${DPOP_SUBPROTOCOL_PREFIX}not*b64u!` })
    expect(buildUpgradeRequest(req, 0).dpop.proof).toBeNull()
  })

  it('parses the comma-separated subprotocol list and always carries the app subprotocol through', () => {
    const req = mkReq({ 'sec-websocket-protocol': `${APP_SUBPROTOCOL}, ${dpopEntry(PROOF)}` })
    expect(buildUpgradeRequest(req, 0).subprotocols).toContain(APP_SUBPROTOCOL)
  })
})

describe('buildUpgradeRequest — activeSessionCount plumbing (F2)', () => {
  it('carries the caller-supplied active-session count verbatim', () => {
    const req = mkReq({ 'sec-websocket-protocol': APP_SUBPROTOCOL })
    expect(buildUpgradeRequest(req, 3).activeSessionCount).toBe(3)
  })
})
