const isProduction = process.env.NODE_ENV === 'production'
const defaultMaxRequestBodyBytes = 50 * 1024 * 1024
export const corsAllowHeaders = 'content-type,authorization,x-admin-token,x-file-name,x-owner-type,x-owner-id,x-file-visibility'

function maxRequestBodyBytes() {
  const configured = Number(process.env.UPLOAD_MAX_BYTES || process.env.MAX_UPLOAD_BYTES || defaultMaxRequestBodyBytes)
  return Number.isFinite(configured) && configured > 0 ? configured : defaultMaxRequestBodyBytes
}

export function requestTooLargeError(limit) {
  const error = new Error(`Request body exceeds ${Math.round(limit / 1024 / 1024)}MB limit`)
  error.statusCode = 413
  error.code = 'request_entity_too_large'
  return error
}

function configuredOrigins() {
  const raw = process.env.CORS_ALLOW_ORIGINS || process.env.FRONTEND_ORIGIN || process.env.WEB_ORIGIN || 'http://localhost:3000'
  return raw.split(',').map((item) => item.trim()).filter(Boolean)
}

export function isCorsOriginAllowed(origin = '') {
  if (!isProduction) return true
  if (!origin) return true
  return configuredOrigins().includes(origin)
}

export function corsHeaders(origin = '') {
  if (!isProduction) return { 'access-control-allow-origin': '*' }
  const origins = configuredOrigins()
  const allowedOrigin = origin && origins.includes(origin) ? origin : origins[0]
  return allowedOrigin ? {
    'access-control-allow-origin': allowedOrigin,
    vary: 'Origin',
  } : {}
}

export function sendJson(response, statusCode, payload, headers = {}) {
  response.writeHead(statusCode, {
    'content-type': 'application/json; charset=utf-8',
    ...corsHeaders(response.corsOrigin || ''),
    'access-control-allow-headers': corsAllowHeaders,
    ...headers,
  })
  response.end(JSON.stringify(payload))
}

export async function readJsonBody(request) {
  const raw = await readRawBody(request)
  if (!raw.trim()) return {}

  try {
    return JSON.parse(raw)
  } catch {
    const error = new Error('Invalid JSON body')
    error.statusCode = 400
    error.code = 'invalid_json'
    throw error
  }
}

export async function readRawBody(request) {
  const chunks = []
  const limit = maxRequestBodyBytes()
  let total = 0
  for await (const chunk of request) {
    total += chunk.length
    if (total > limit) throw requestTooLargeError(limit)
    chunks.push(chunk)
  }
  return Buffer.concat(chunks).toString('utf8')
}

export async function readMultipartFile(request) {
  const contentType = request.headers['content-type'] || ''
  const boundaryMatch = contentType.match(/boundary=(?:"([^"]+)"|([^;]+))/i)
  if (!boundaryMatch) {
    const error = new Error('Missing multipart boundary')
    error.statusCode = 400
    error.code = 'invalid_multipart'
    throw error
  }

  const chunks = []
  const limit = maxRequestBodyBytes()
  let total = 0
  for await (const chunk of request) {
    total += chunk.length
    if (total > limit) throw requestTooLargeError(limit)
    chunks.push(chunk)
  }
  const body = Buffer.concat(chunks)
  const boundary = Buffer.from(`--${boundaryMatch[1] || boundaryMatch[2]}`)
  let cursor = body.indexOf(boundary)

  while (cursor !== -1) {
    const headerStart = cursor + boundary.length + 2
    const headerEnd = body.indexOf(Buffer.from('\r\n\r\n'), headerStart)
    if (headerEnd === -1) break

    const header = body.slice(headerStart, headerEnd).toString('utf8')
    const nextBoundary = body.indexOf(boundary, headerEnd + 4)
    if (nextBoundary === -1) break

    const dataEnd = nextBoundary - 2
    const data = body.slice(headerEnd + 4, Math.max(headerEnd + 4, dataEnd))
    const disposition = header.match(/content-disposition:[^\r\n]+/i)?.[0] || ''
    const name = disposition.match(/name="([^"]+)"/i)?.[1] || ''
    const fileName = disposition.match(/filename="([^"]*)"/i)?.[1] || ''

    if (name === 'file' && fileName) {
      const mimeType = header.match(/content-type:\s*([^\r\n]+)/i)?.[1]?.trim() || 'application/octet-stream'
      return { fileName, mimeType, buffer: data }
    }

    cursor = nextBoundary
  }

  const error = new Error('No file uploaded')
  error.statusCode = 400
  error.code = 'missing_file'
  throw error
}

export function getBearerToken(request) {
  const header = request.headers.authorization || ''
  const match = header.match(/^Bearer\s+(.+)$/i)
  return match?.[1] || ''
}

export function notFound(response) {
  sendJson(response, 404, {
    error: 'not_found',
    message: 'Route not found',
  })
}

export function methodNotAllowed(response) {
  sendJson(response, 405, {
    error: 'method_not_allowed',
    message: 'Method not allowed',
  })
}
