package main

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

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

// BackendName is the name of our service backend.
const BackendName = "http_me"

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		
    // Get client IP
    clientIP := net.ParseIP(r.RemoteAddr)
    // Lookup geo meta data associated with the client IP address.
    geo, err := geo.Lookup(clientIP)
    if err != nil {returnErrorResponse(w, fsthttp.StatusInternalServerError, err)}

    // Get geo country code from client IP address
    countryCode := geo.CountryCode
    // Get country's regions info from the config store
    regionDefs, err := configstore.Open("region_defs")
    if err != nil {returnErrorResponse(w, fsthttp.StatusInternalServerError, err)}
    regionVal, err := regionDefs.Get(countryCode)
    if err != nil {returnErrorResponse(w, fsthttp.StatusInternalServerError, err)}

    fmt.Println("Country:", countryCode, ", Region:", regionVal)

    if regionVal == "blocked" {
      w.WriteHeader(fsthttp.StatusForbidden)
      io.Copy(w, strings.NewReader("Sorry, our service is currently not available in your region."))
      //w.Append(io.NopCloser(strings.NewReader("Sorry, our service is currently not available in your region.")))
      return
    }

    // Set country/region/continent information on request header
    r.Header.Add("client-geo-country", countryCode)
    r.Header.Add("client-geo-region", regionVal)
    r.Header.Add("client-geo-continent", geo.ContinentCode)

    fmt.Println("Continent:", geo.ContinentCode)

    // Get latitude and longitude from Fastly Geo API
    lat := geo.Latitude
    long := geo.Longitude

    // If the latidue and longitude already exist from a cookie value, overwrite it with the cookie value.
    geoCookie, err := r.Cookie("client-geo-latlng")
    if err != nil {
      if err == fsthttp.ErrNoCookie {
        // Do nothing
      } else {
        // Any other error return 500 response
        returnErrorResponse(w, fsthttp.StatusInternalServerError, err)
      }
    }
    // If geoCookie exists, url decode it and parse it.
    if len(geoCookie.String()) > 0 {
      decodeGeoCookie, err := url.QueryUnescape(geoCookie.Value)
      if err != nil {returnErrorResponse(w, fsthttp.StatusInternalServerError, err)}
      // Remove any whitespaces if any
      decodeGeoCookie = strings.ReplaceAll(decodeGeoCookie, " ", "")
      // Split string into comma separated string slices
      s := strings.Split(decodeGeoCookie, ",")
      if lat, err = strconv.ParseFloat(s[0], 64); err != nil {returnErrorResponse(w, fsthttp.StatusInternalServerError, err)}
      if long, err = strconv.ParseFloat(s[1], 64); err != nil {returnErrorResponse(w, fsthttp.StatusInternalServerError, err)}
    }

    // Add the latitude and the longitude as a request header
    r.Header.Add("client-geo-latlng", fmt.Sprintf("%.1f, %.1f", lat, long))
    fmt.Println("Lat:", lat, "Lng:", long)
   
    resp, err := r.Send(ctx, BackendName)
    if err != nil {returnErrorResponse(w, fsthttp.StatusBadGateway, err)}

		w.Header().Reset(resp.Header)
		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)
	})
}

func returnErrorResponse(w fsthttp.ResponseWriter, code int, err error) {
  w.WriteHeader(code)
  fmt.Fprintln(w, err.Error())
  return
}