package main

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"

	"code.justin.tv/live/backend-fundamentals/compute/logic"
	"code.justin.tv/live/backend-fundamentals/compute/schema"
)

func main() {
	server := &http.Server{
		Addr: ":8080",
		Handler: &PrimeHandler{
			logic: &logic.Logic{},
		},
	}

	log.Println("Serving on port 8080")
	err := server.ListenAndServe()
	if err != nil {
		log.Println(err)
		return
	}

	log.Println("Shutting down")
}

// PrimeHandler implements the http.Handler interface.
type PrimeHandler struct {
	logic *logic.Logic
}

var _ http.Handler = &PrimeHandler{}

func (p *PrimeHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		http.NotFound(resp, req)
		return
	}

	switch req.URL.Path {
	case "/isprime":
		p.handleIsPrime(resp, req)

	default:
		http.NotFound(resp, req)
		return
	}
}

func (p *PrimeHandler) handleIsPrime(resp http.ResponseWriter, req *http.Request) {
	// Read the request body, getting JSON encoded as bytes.
	requestBodyBytes, err := ioutil.ReadAll(req.Body)
	if err != nil {
		writeError(resp, err)
		return
	}
	err = req.Body.Close()
	if err != nil {
		log.Println(err.Error())
	}

	// Unmarshal the JSON into a struct that we can work with.
	var isPrimeRequest schema.IsPrimeRequest
	err = json.Unmarshal(requestBodyBytes, &isPrimeRequest)
	if err != nil {
		writeError(resp, err)
	}

	// Do the business logic.
	isPrimeResponse, err := p.logic.IsPrime(&isPrimeRequest)
	if err != nil {
		writeError(resp, err)
		return
	}

	// Convert the response struct to JSON bytes.
	responseBytes, err := json.Marshal(&isPrimeResponse)
	if err != nil {
		writeError(resp, err)
		return
	}

	// Set response headers.
	resp.Header().Set("Content-Type", "application/json")
	resp.Header().Set("Content-Length", strconv.Itoa(len(responseBytes)))
	resp.WriteHeader(http.StatusOK)

	// Write the JSON bytes to the HTTP response.
	byteReader := bytes.NewBuffer(responseBytes)
	io.Copy(resp, byteReader)

}

func writeError(resp http.ResponseWriter, err error) {
	resp.WriteHeader(http.StatusInternalServerError)

	stringReader := strings.NewReader(err.Error())
	io.Copy(resp, stringReader)
}
