/// <reference types="@fastly/js-compute" />
import { EsiTransformStream } from "@fastly/esi";

// We assume here that the source page's ESI includes are in the form
// /__since/<unix-timestamp>, and we need to convert that into
// a relative time
const sinceRegex = /^\/__since\/(\d+)$/;

addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));

async function handleRequest(event) {
  const req = event.request;

  const headers = new Headers(req.headers);

  // Make sure the origin server does not gzip the
  // response. We can't process ESIs in gzipped content
  headers.delete('accept-encoding');

  // Make a backend request
  const beresp = await fetch(req.url, {
    headers,
    backend: 'origin_0'
  });

  // It is pointless performing ESI on non-HTML responses, so this is
  // a good starting point for making the ESI invocation appropriately
  // conditional.  However, you could make it tighter still if there
  // are only specific pages on which you use ESI.
  if (!beresp.headers.get('Content-Type')?.startsWith('text/html')) {
    return beresp;
  }

  // Create an ESI transformation stream designed to handle the special
  // ESI tag
  const esiTransformStream = new EsiTransformStream(req.url, headers, {
    fetch(input, init) {

      // @fastly/esi allows us to replace the fetch() function that would
      // be caused by an esi:include tag. Here we examine the URL path,
      // and return a synthetic Response().
      const url = new URL(input instanceof Request ? input.url : String(input));
      const sinceMatch = sinceRegex.exec(url.pathname);
      if (sinceMatch != null) {
        const timestampString = sinceMatch[1];
        const timestamp = parseInt(timestampString, 10);
        return new Response(
          toRelativeTimeSince(timestamp),
          {
            headers: {
              'Content-Type': 'text/plain',
            },
            status: 200,
          }
        );
      }

      // For all other esi:include tags,
      // add the backend and call the global fetch
      return fetch(input, { ...init, backend: 'origin_0' });
    }
  });

  // Pass the backend response through the ESI transformation
  // and return the response
  return new Response(
    beresp.body.pipeThrough(esiTransformStream),
    {
      status: beresp.status,
      headers: beresp.headers,
    },
  );
}

// Generates a relative time string since the passed-in unix timestamp
function toRelativeTimeSince(timestamp) {
  const now = Date.now() / 1000;
  const diff = now - timestamp;

  if (diff < 60) {
    const seconds = Math.floor(diff);
    return `${seconds}s ago`;
  }
  if (diff < 60 * 60) {
    const minutes = Math.floor(diff / 60);
    return `${minutes}m ago`;
  }
  if (diff < 60 * 60 * 24) {
    const hours = Math.floor(diff / (60 * 60));
    return `${hours}h ago`;
  }
  const days = Math.floor(diff / (60 * 60 * 24));
  return `${days}d ago`;
}