package main

import (
	"context"
	"fmt"
  "net/url"
	"io"

	"github.com/fastly/compute-sdk-go/fsthttp"
)

// BackendName is the name of our service backend.
const ContentBackend = "origin_0"
const PaywallBackend = "origin_1"

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
    // Ingest cookies
    // Getting a session ID from a cookie is rather simplistic in this
    // demo.  For a more advanced solution, combine the paywall pattern
    // with a JSON web token verification at the edge
    cookie, err := r.Cookie("sessionid")
    if err == nil {
      r.Header.Del("cookie")
      r.Header.Set("auth-sessionid", cookie.Value)
    }
    // Only GETs and HEADs are considered for paywall restriction.    
    if r.Method != "GET" && r.Method != "HEAD" {
		  resp, err := r.Send(ctx, ContentBackend)
      if err != nil {
			  w.WriteHeader(fsthttp.StatusBadGateway)
			  fmt.Fprintln(w, err.Error())
			  return        
      }
		  w.Header().Reset(resp.Header)
		  w.WriteHeader(resp.StatusCode)
		  io.Copy(w, resp.Body)
      return
    }
    // request bodies will not be forwarded.
    r.Body = nil;
		content_resp, err := r.Send(ctx, ContentBackend)
    if err != nil {
			w.WriteHeader(fsthttp.StatusBadGateway)
			fmt.Fprintln(w, err.Error())
			return
		}
    paywall_hdr := content_resp.Header.Get("paywall")  
    // if 403 or 5xx - don't continue
    // If the response contains `paywall` header we run a check against the URL
    // No paywall header received, deliver the content
    if content_resp.StatusCode == 403 || content_resp.StatusCode >= 500 || paywall_hdr == "" {
		  w.Header().Reset(content_resp.Header)
		  w.WriteHeader(content_resp.StatusCode)
		  io.Copy(w, content_resp.Body)
      return
    }

    content_resp.Header.Del("paywall")
    paywall_url, err := url.Parse(paywall_hdr)
    if err != nil {
			w.WriteHeader(fsthttp.StatusBadGateway)
			fmt.Fprintln(w, err.Error())
			return
		}
    if paywall_url.Host != "fiddle-paywall.glitch.me" {
      // Not an expected paywall, deliver the content as is
		  w.Header().Reset(content_resp.Header)
		  w.WriteHeader(content_resp.StatusCode)
		  io.Copy(w, content_resp.Body)
      return      
    }
    // First, re-create a request with original headers we received from client
    req_paywall := r.Clone()
    req_paywall.URL = paywall_url
    // run the request against the paywall and get a response
		paywall_resp, err := req_paywall.Send(ctx, PaywallBackend)
    if err != nil {
			w.WriteHeader(fsthttp.StatusBadGateway)
			fmt.Fprintln(w, err.Error())
			return
		}
    // Check the paywall response
    if paywall_resp.Header.Get("paywall-result") == "BLOCK" {
      // if answer is `BLOCK` - deliver an error
      w.WriteHeader(fsthttp.StatusForbidden)
      w.Header().Set("content-type", "text/html")
      fmt.Fprintln(w, "<p>This content is only available to premium subscribers.</p>\n")
      return
    }
    if paywall_resp.Header.Get("paywall-result") != "" {
      paywall_meta := paywall_resp.Header.Get("paywall-meta")
      content_resp.Header.Set("paywall-meta", paywall_meta)
    }
  
		w.Header().Reset(content_resp.Header)
		w.WriteHeader(content_resp.StatusCode)
		io.Copy(w, content_resp.Body)
	})
}