package main

import (
	"bufio"
	"bytes"
	"context"
	"crypto/sha1"
	"fmt"
	"io"
	"net/url"

	"strings"

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

// The name of the backend servers associated with this service.
// This must match the backend names you configured using `fastly backend create`.
const (
	BACKEND_APP_SERVER     = "origin_0"
	BACKEND_SECURITY_CHECK = "origin_1"
	PREFIX_LENGTH          = 5

	LOGIN_HTML = `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Compromised password detection demo</title>
  </head>
  <body>
    <form action="/post" method="post">
      <div class="container">
        <label for="username"><b>Username</b></label>
        <input type="text" placeholder="Enter Username" name="username" required />

        <label for="password"><b>Password</b></label>
        <input type="password" placeholder="Enter Password" name="password" required />

        <button type="submit">Login</button>
      </div>
    </form>
  </body>
</html>`
)

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		if r.Method == fsthttp.MethodGet && r.URL.Path == "/" {
			w.WriteHeader(fsthttp.StatusOK)
			fmt.Fprint(w, LOGIN_HTML)
			return
		}

		// Read the request body and parse the form fields to get the plainCred
		reqBody, _ := io.ReadAll(r.Body)
		vals, _ := url.ParseQuery(string(reqBody))
		plainCred := vals.Get("password")

		// generate a sha1 hash of the credential and convert to an uppercase hexadecimal string
		hashedCredByteArray := sha1.Sum([]byte(plainCred))
		hashedCred := strings.ToUpper(fmt.Sprintf("%x", hashedCredByteArray))

		// Split the hash of credential to left and right part at position PREFIX_LENGTH
		hashedLeft, hashedRight := hashedCred[:PREFIX_LENGTH], hashedCred[PREFIX_LENGTH:]

		// Prepare the request for threat check
		// (If you use HIBP in production please use an API key)
		apiUrl := fmt.Sprintf("https://api.pwnedpasswords.com/range/%s", hashedLeft)
		apiReq, err := fsthttp.NewRequest(fsthttp.MethodGet, apiUrl, nil)
		if err != nil {
			w.WriteHeader(fsthttp.StatusInternalServerError)
			fmt.Fprintf(w, "error creating HIBP API request")
		}

		// Send threat check request to API with the left-hand-side of the SHA1 hash
		apiResp, _ := apiReq.Send(ctx, BACKEND_SECURITY_CHECK)
		if err != nil {
			w.WriteHeader(fsthttp.StatusInternalServerError)
			fmt.Fprintf(w, "error making HIBP API request")
		}

		// Check if the response body contains the right-hand-side of the sha1 hash
		status := "safe-credential"
		scanner := bufio.NewScanner(apiResp.Body)
		for scanner.Scan() {
			if scanner.Text()[:len(hashedRight)] == hashedRight {
				status = "compromised-credential"
				break
			}
		}
		r.Header.Add("fastly-password-status", status)

		// Reset the request body by creating a new io.ReaderCloser
		r.Body = io.NopCloser(bytes.NewReader(reqBody))

		// Send request to the primary origin as normal
		resp, _ := r.Send(ctx, BACKEND_APP_SERVER)

		w.WriteHeader(resp.StatusCode)
		io.Copy(w, resp.Body)

	})
}