const TEMPLATES = {
  login: async () => 'Hi, Logged in user!',
  homeStore: async () => 'Your local store is open until 18:00 today',
}

class ReplacerStream extends TransformStream {
  constructor(substitutions) {
    super({
      transform: async(chunk, controller) => {
        chunk = this.buffer + this.textDecoder.decode(chunk);

        for (const [key, tmplFn] of Object.entries(this.substitutions)) {
          const target = '{{' + key + '}}';
          while (chunk.includes(target)) {
            const pos = chunk.indexOf(target);
            chunk = chunk.substring(0,pos) + (await tmplFn()) + chunk.substring(pos + target.length);
          }
        }

        this.buffer = chunk.substr(0 - this.bufferLen);
        controller.enqueue(
          this.textEncoder.encode(
            chunk.substr(0, chunk.length - this.buffer.length)
          )
        );
      }
    });
    this.substitutions = substitutions;
    this.textEncoder = new TextEncoder();
    this.textDecoder = new TextDecoder();
    this.buffer = '';
    this.bufferLen = Object.keys(substitutions).reduce((maxLen, k) => Math.max(maxLen, k.length), 0) + 4;
  }
}

async function handleRequest(req) {
  const templateEngine = new ReplacerStream(TEMPLATES);

  const beResp = await fetch(req, { backend: 'origin_0' });
  let resp = beResp;

  const isHTML = (beResp.headers.get('content-type') ?? '').startsWith('text/html');
  if (isHTML && beResp.body) {
    const newBody = beResp.body.pipeThrough(templateEngine);
    resp = new Response(newBody, {
      headers: { ...beResp.headers, 'cache-control': 'no-store, private' }
    });
  }

  return resp;
}

addEventListener('fetch', ev => ev.respondWith(handleRequest(ev.request)));