package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"
)

const defaultPort = "8080"

var colorServiceEndpoint = ""

var hostsStats []hostStatusCounter
var counterMutex = &sync.Mutex{}

type hostStatusCounter struct {
	HostUID string
	Counter struct {
		StatusOk    int
		StatusError int
		Total       int
	}
}

func getServerPort() string {
	port := os.Getenv("SERVER_PORT")
	if port != "" {
		return port
	}
	return defaultPort
}

func getColorServiceEndpoint() (string, error) {
	endpoint := os.Getenv("COLOR_SERVICE_ENDPOINT")
	if endpoint != "" {
		return endpoint, nil
	}
	return "", errors.New("COLOR_SERVICE_ENDPOINT is not set")
}

func updateStats(hostUID string, status int) {
	counterMutex.Lock()
	defer counterMutex.Unlock()
	found := false
	for i, h := range hostsStats {
		if h.HostUID == hostUID {
			hostsStats[i].Counter.Total++
			if status == http.StatusOK {
				hostsStats[i].Counter.StatusOk++
			} else {
				hostsStats[i].Counter.StatusError++
			}
			found = true
		}
	}
	if !found {
		var newHost hostStatusCounter
		if status == http.StatusOK {
			newHost = hostStatusCounter{
				HostUID: hostUID,
				Counter: struct {
					StatusOk    int
					StatusError int
					Total       int
				}{
					StatusOk:    1,
					StatusError: 0,
					Total:       1,
				},
			}

		} else {
			newHost = hostStatusCounter{
				HostUID: hostUID,
				Counter: struct {
					StatusOk    int
					StatusError int
					Total       int
				}{
					StatusOk:    0,
					StatusError: 1,
					Total:       1,
				},
			}
		}
		hostsStats = append(hostsStats, newHost)
	}
	fmt.Println("stats updated: ", hostsStats)
}

func main() {
	log.Printf("starting server on port %s\n", getServerPort())
	c, err := getColorServiceEndpoint()
	if c == "" {
		log.Fatalln(err)
	}
	colorServiceEndpoint = c
	http.HandleFunc("/ping", pingHandler)
	http.HandleFunc("/color/get", colorGetHandler)
	http.HandleFunc("/color/fault", colorFaultHandler)
	http.HandleFunc("/color/recover", colorRecoverHandler)
	http.HandleFunc("/stats", statsHandler)
	http.HandleFunc("/reset_stats", resetStatsHandler)
	log.Fatal(http.ListenAndServe(":"+getServerPort(), nil))
}

func pingHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("received ping.")
}

func colorServiceHandler(w http.ResponseWriter, r *http.Request, path string) {
	log.Printf("received color/%s request.", path)
	resp, err := http.Get(fmt.Sprintf("http://%s/%s", colorServiceEndpoint, path))
	if err != nil {
		log.Printf("error in getting response: %s\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, err.Error())
		return
	}
	if path == "get" {
		updateStats(resp.Header.Get("HostUID"), resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	w.WriteHeader(resp.StatusCode)
	fmt.Fprintf(w, "%s\n", string(body))
}

func colorGetHandler(w http.ResponseWriter, r *http.Request) {
	colorServiceHandler(w, r, "get")
}

func colorFaultHandler(w http.ResponseWriter, r *http.Request) {
	colorServiceHandler(w, r, "fault")
}

func colorRecoverHandler(w http.ResponseWriter, r *http.Request) {
	colorServiceHandler(w, r, "recover")
}

func statsHandler(w http.ResponseWriter, r *http.Request) {
	json, _ := json.Marshal(hostsStats)
	fmt.Fprintf(w, "%s\n", string(json))
}

func resetStatsHandler(w http.ResponseWriter, r *http.Request) {
	counterMutex.Lock()
	defer counterMutex.Unlock()
	hostsStats = nil
	fmt.Fprintf(w, "stats cleared.\n")
}