import { join, relative } from 'node:path'

export default {
  /**
   * Fetch and log a request
   * @param {Request} request
   */
  async fetch (request) {
    const { pathname } = new URL(request.url)

    if (pathname === '/images/favicon.ico') {
      return new Response('', {
        status: 404,
        headers: {
          'Cache-Control': 'no-cache'
        }
      })
    }

    if (/image-resizing/.test(request.headers.get("via"))) {
      return new Response('Request loop', {
        status: 403,
        headers: {
          'Cache-Control': 'no-cache'
        }
      })
    }

    // https://developers.cloudflare.com/images/url-format#supported-formats-and-limitations
    if (!/\.(jpe?g|png|gif|webp)$/i.test(pathname)) {
      return new Response('Invalid file extension', {
        status: 400,
        headers: {
          'Cache-Control': 'no-cache'
        }
      })
    }

    // Cloudflare-specific options are in the cf object.
    const options = { cf: { image: {} } }

    const numParts = pathname.split('/').length
    let imgPath = ''
    // original size
    if (numParts === 4) {
      imgPath = relative('/images', pathname)
    } else if (numParts === 5) {
      // Copy width size from path to request options
      const width = relative('/images', pathname).split('/')[0]
      const validSizes = new Set(['320', '468', '768'])

      if (validSizes.has(width)) {
        imgPath = relative(join('/images', width), pathname)
        options.cf.image.width = width
        // serve original size if width is larger
        options.cf.image.fit = 'scale-down'
      } else {
        return new Response('Invalid width', {
          status: 400,
          headers: {
            'Cache-Control': 'no-cache'
          }
        })
      }
    } else {
      return new Response('Invalid path', {
        status: 404,
        headers: {
          'Cache-Control': 'no-cache'
        }
      })
    }

    // Your Worker is responsible for automatic format negotiation. Check the Accept header.
    const accept = request.headers.get('Accept')
    if (/image\/avif/.test(accept)) {
      options.cf.image.format = 'avif'
    } else if (/image\/webp/.test(accept)) {
      options.cf.image.format = 'webp'
    }

    // Build a request that passes through request headers
    // Images are stored on https://gitlab.com/curben/blog/-/tree/site
    const imageURL = new URL(imgPath, 'https://mdleom.com/')
    const imageRequest = new Request(imageURL, {
      headers: request.headers
    })

    let response = await fetch(imageRequest, options)
    // Reconstruct the Response object to make its headers mutable.
    response = new Response(response.body, response)

    if (response.ok || response.redirected) {
      // Set cache for 1 week
      response.headers.set('Cache-Control', 'max-age=604800, public')
      // Set Vary header
      response.headers.set('Vary', 'Accept')
      return response
    } else if (response.status === 404) {
      // Custom 404 page
      const { readable, writable } = new TransformStream()
      const { status, statusText } = response

      const htmlHeader = new Headers(request.headers)
      if (!/text\/html/.test(accept)) htmlHeader.append('Accept', 'text/html')
      const page404 = new Request('https://mdleom.com/404', {
        headers: htmlHeader
      })
      const res404 = await fetch(page404)
      res404.body.pipeTo(writable)

      return new Response(readable, {
        status,
        statusText,
        headers: {
          ...res404.headers,
          'Cache-Control': 'no-cache',
          'Content-Type': 'text/html; charset=utf-8'
        }
      })
    } else {
      return new Response(`Could not fetch the image, the origin returned HTTP error ${response.status}`, {
        status: response.status,
        headers: {
          'Cache-Control': 'no-cache'
        }
      })
    }
  }
}
