import { IncomingMessage, ServerResponse } from 'node:http'
import { Socket } from 'node:net'
import { createEvent } from 'h3'

/**
 * Create a real h3 event backed by real Node.js request/response objects.
 *
 * Options:
 *   method        - HTTP method (default 'GET')
 *   path          - Request path (default '/')
 *   headers       - Object of request headers
 *   cookies       - Object of cookie key/value pairs (serialized to cookie header)
 *   body          - Request body (will be JSON-serialized)
 *   remoteAddress - Client IP (default '127.0.0.1')
 */
export function createMockEvent(options = {}) {
  const {
    method = 'GET',
    path = '/',
    headers = {},
    cookies = {},
    body = undefined,
    remoteAddress = '127.0.0.1'
  } = options

  // Build cookie header from cookies object
  const cookiePairs = Object.entries(cookies).map(([k, v]) => `${k}=${v}`)
  if (cookiePairs.length > 0) {
    headers.cookie = [headers.cookie, cookiePairs.join('; ')].filter(Boolean).join('; ')
  }

  // Build a real IncomingMessage
  const socket = new Socket()
  // remoteAddress is a getter-only property on Socket, so use defineProperty
  Object.defineProperty(socket, 'remoteAddress', {
    value: remoteAddress,
    writable: true,
    configurable: true
  })
  const req = new IncomingMessage(socket)
  req.method = method
  req.url = path
  req.headers = Object.fromEntries(
    Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])
  )

  // If body is provided, push it into the request stream so readBody can consume it
  if (body !== undefined) {
    const bodyStr = typeof body === 'string' ? body : JSON.stringify(body)
    req.headers['content-type'] = req.headers['content-type'] || 'application/json'
    req.headers['content-length'] = Buffer.byteLength(bodyStr).toString()
    req.push(bodyStr)
    req.push(null)
  }

  const res = new ServerResponse(req)

  // Capture response headers for test assertions
  const setHeaders = {}
  const originalSetHeader = res.setHeader.bind(res)
  res.setHeader = (name, value) => {
    setHeaders[name.toLowerCase()] = value
    return originalSetHeader(name, value)
  }

  const event = createEvent(req, res)

  // Attach captured headers for test access
  event._testSetHeaders = setHeaders

  return event
}
