package main

import (
	"bufio"
	"bytes"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

type OneMetric struct {
	Deveui               string
	DeveuiJS             template.JS
	Alias                string
	Readonly             bool
	Smsalarmactive       string
	Smsnumber            string
	Emailalarmactive     string
	Email                string
	Greenzone            string
	Timestamp            string
	PosTimestamp         string
	Lat                  string
	Lon                  string
	LatJS                template.JS
	LonJS                template.JS
	Vbat                 string
	Fw                   string
	BatteryPercent       string
	BatteryPercentHTML   template.HTML
	ActiveUntil          string
	DaysUntilDeactivated int // berechneter Wert
}

// global variables
var INFLUX_RO_TOKEN = os.Getenv("INFLUX_RO_TOKEN")

// metrics handler

func string2float64(s string) float64 {
	f, err := strconv.ParseFloat(s, 64)
	if err == nil {
		return f
	}
	return 0
}

func metricsHandler(response http.ResponseWriter, request *http.Request) {
	name := getUserName(request)
	if name != "" {

		deveui, ok := request.URL.Query()["deveui"]

		if !ok || len(deveui[0]) < 1 {
			log.Println("Url Param 'deveui' is missing")
			fmt.Fprintf(response, "{ \"msg\": \"error: deveui must be specified in URL\" }")
			return
		}
		// Query()["deveui"] will return an array of items,
		// we only want the single item.
		mydeveui := deveui[0]

		if !(Contains(getMyDevs(name), mydeveui)) {
			log.Println("specified 'deveui' does not belong to this user")
			fmt.Fprintf(response, "{ \"msg\": \"error: specified deveui does not belong to this user\" }")
			return
		}

		if AboExpired(mydeveui) {
			log.Println("specified 'deveui' has an expired abo")
			fmt.Fprintf(response, "{ \"msg\": \"specified deveui has an expired abo\" }")
			return
		}

		log.Println("Url Param 'deveui' is: " + string(mydeveui))

		// Format of start and stop: YYYY-MM-DDTHH:MI:SSZ

		stop, ok := request.URL.Query()["stop"]
		var mystop string
		if !ok || len(stop[0]) < 1 {
			log.Println("Url Param 'stop' is missing, set it to now")
			mystop = time.Now().Format("2006-01-02T15:04:05Z")
		}

		if ok {
			mystop = stop[0]
		}

		layout := "2006-01-02T15:04:05Z"
		stopDate, err := time.Parse(layout, mystop)

		if err != nil {
			fmt.Println(err)
		}

		start, ok := request.URL.Query()["start"]
		var mystart string
		if !ok || len(start[0]) < 1 {
			log.Println("Url Param 'start' is missing, set it to stop  minus one day")
			t := stopDate.AddDate(0, 0, -1)
			mystart = t.Format("2006-01-02T15:04:05Z")
		}

		if ok {
			mystart = start[0]
		}

		url := getenv("INFLUX_URL", "http://localhost:8086/api/v2/query?org=wobischorg")
		data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") 
                                         |> range(start: %s, stop: %s) 
                                         |> filter(fn: (r) => r._measurement == "measurement") 
                                         |> filter(fn: (r) => r.deveui == "%s")
                                         |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
                                         |> filter(fn: (r) => r.lat != 0)`, mystart, mystop, mydeveui))

		req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
		if err != nil {
			log.Fatal("Error reading request. ", err)
		}

		// Set headers
		req.Header.Set("Authorization", "Token "+INFLUX_RO_TOKEN)
		req.Header.Set("accept", "application/csv")
		req.Header.Set("content-type", "application/vnd.flux")

		// Set client timeout
		client := &http.Client{Timeout: time.Second * 10}

		// Send request
		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Error reading response. ", err)
		}
		defer resp.Body.Close()

		fmt.Println("response Status:", resp.Status)
		fmt.Println("response Headers:", resp.Header)

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatal("Error reading body. ", err)
		}
		fmt.Println("response Body:", string(body))

		scanner := bufio.NewScanner(strings.NewReader(string(body)))
		first := true

		var lat float64
		var lon float64
		var min_lat float64 = 9999999999.9
		var min_lon float64 = 9999999999.9
		var max_lat float64 = 0
		var max_lon float64 = 0

		fmt.Fprintf(response, "{\n")
		fmt.Fprintf(response, "  \"data\": [\n")
		for scanner.Scan() {
			s := strings.Split(scanner.Text(), ",")
			fmt.Printf("Scanned Line: %v, %d elements\n", s, len(s))
			if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) {
				t, err := time.Parse(time.RFC3339, s[5])
				if err != nil {
					fmt.Printf("error converting time: %s\n", s[5])
					continue
				}
				a := t.Unix()
				lat = string2float64(s[len(s)-3])
				lon = string2float64(s[len(s)-2])
				if (lat == 0) || (lon == 0) {
					fmt.Println("skip 0 value for lon/lat")
				} else {
					if lat < min_lat {
						min_lat = lat
					}
					if lon < min_lon {
						min_lon = lon
					}
					if lat > max_lat {
						max_lat = lat
					}
					if lon > max_lon {
						max_lon = lon
					}
					if !(first) {
						fmt.Fprintf(response, "           ,")
					} else {
						first = false
						fmt.Fprintf(response, "            ")
					}

					fmt.Fprintf(response, "[%d, %s, %s, %s, %d]\n", a, s[len(s)-3], s[len(s)-2], s[len(s)-1], vbat2percent(s[len(s)-1]))

				}

			}
		}
		fmt.Fprintf(response, "          ],\n")
		fmt.Fprintf(response, "  \"min_lat\": %f,\n", min_lat)
		fmt.Fprintf(response, "  \"min_lon\": %f,\n", min_lon)
		fmt.Fprintf(response, "  \"max_lat\": %f,\n", max_lat)
		fmt.Fprintf(response, "  \"max_lon\": %f\n", max_lon)
		fmt.Fprintf(response, "}\n")

	} else {
		fmt.Fprintf(response, "{ \"msg\": \"Only available for logged in users\" }")
	}
}

func downloadmetricsHandler(response http.ResponseWriter, request *http.Request) {
	name := getUserName(request)
	if name != "" {

		deveui, ok := request.URL.Query()["deveui"]

		if !ok || len(deveui[0]) < 1 {
			log.Println("Url Param 'deveui' is missing")
			fmt.Fprintf(response, "Error: deveui is missing")
			return
		}
		// Query()["deveui"] will return an array of items,
		// we only want the single item.
		mydeveui := deveui[0]

		if !(Contains(getMyDevs(name), mydeveui)) {
			log.Println("specified 'deveui' does not belong to this user")
			fmt.Fprintf(response, "Error: specified deveui does not belong to this user")
			return
		}

		if AboExpired(mydeveui) {
			log.Println("specified 'deveui' has an expired abo")
			fmt.Fprintf(response, "Error: specified deveui has an expired abo")
			return
		}

		log.Println("Url Param 'deveui' is: " + string(mydeveui))

		// Format of start and stop: YYYY-MM-DDTHH:MI:SSZ

		stop, ok := request.URL.Query()["stop"]
		var mystop string
		if !ok || len(stop[0]) < 1 {
			log.Println("Url Param 'stop' is missing, set it to now")
			mystop = time.Now().Format("2006-01-02T15:04:05Z")
		}

		if ok {
			mystop = stop[0]
		}

		layout := "2006-01-02T15:04:05Z"
		stopDate, err := time.Parse(layout, mystop)

		if err != nil {
			fmt.Println(err)
		}

		start, ok := request.URL.Query()["start"]
		var mystart string
		if !ok || len(start[0]) < 1 {
			log.Println("Url Param 'start' is missing, set it to stop  minus one day")
			t := stopDate.AddDate(0, 0, -1)
			mystart = t.Format("2006-01-02T15:04:05Z")
		}

		if ok {
			mystart = start[0]
		}

		url := getenv("INFLUX_URL", "http://localhost:8086/api/v2/query?org=wobischorg")
		data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") 
                                         |> range(start: %s, stop: %s) 
                                         |> filter(fn: (r) => r._measurement == "measurement") 
                                         |> filter(fn: (r) => r.deveui == "%s")
                                         |> filter(fn: (r) => r._field == "lon" or r._field == "lat" or r._field == "flags" or r._field == "vbat") 
                                         |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")`, mystart, mystop, mydeveui))

		req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
		if err != nil {
			log.Fatal("Error reading request. ", err)
		}

		// Set headers
		req.Header.Set("Authorization", "Token "+INFLUX_RO_TOKEN)
		req.Header.Set("accept", "application/csv")
		req.Header.Set("content-type", "application/vnd.flux")

		// Set client timeout
		client := &http.Client{Timeout: time.Second * 10}

		// Send request
		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Error reading response. ", err)
		}
		defer resp.Body.Close()

		fmt.Println("response Status:", resp.Status)
		fmt.Println("response Headers:", resp.Header)

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatal("Error reading body. ", err)
		}
		fmt.Println("response Body:", string(body))
		location, err := time.LoadLocation("Europe/Zurich")

		scanner := bufio.NewScanner(strings.NewReader(string(body)))

		for scanner.Scan() {
			s := strings.Split(scanner.Text(), ",")
			fmt.Printf("Scanned Line: %v, elements: %d\n", s, len(s))
			if (len(s) >= 11) && !(strings.HasPrefix(s[5], "_")) {
				s_flags := ""
				lat := s[len(s)-3]
				lon := s[len(s)-2]
				if lat == "0" {
					s_flags = "NOGPS"
				} else {
					s_flags = "GPS"
				}
				if len(s) == 12 {
					flags, _ := strconv.Atoi(s[len(s)-4])
					if flags&1 == 1 {
						s_flags = s_flags + "+ALARM_BUTTON"
					}
					if flags&2 == 2 && lat != "0" {
						s_flags = s_flags + "+OUTSIDE_GREENZONE"
					}
				}
				t, err := time.Parse(time.RFC3339, s[5])
				if err != nil {
					fmt.Printf("error converting time: %s\n", s[5])
					continue
				}
				mytime := t.In(location).Format("02.01.2006 15:04")
				vbat := s[len(s)-1]
				percent := vbat2percent(vbat)
				fmt.Fprintf(response, "\"%s\",%s,%s,%d,%s\n", mytime, lon, lat, percent, s_flags)

			}
		}

	} else {
		fmt.Fprintf(response, "Only available for logged in Users")
	}
}

func vbat2percent(vbat string) int {
	i, err := strconv.Atoi(vbat)
	res := 0
	if err == nil {
		res = int(float64(i-3400) / 6.0)
		fmt.Printf("vbat2percent Result in Percent: %d\n", res)
		if res < 0 {
			res = 0
		} else if res > 100 {
			res = 100
		}
	}

	return res
}

func vbat2html(vbat string) string {
	percent := vbat2percent(vbat)
	fa_battery_string := ""
	icon_color := ""

	if percent <= 20 {
		fa_battery_string = "fa-battery-empty"
		icon_color = "has-background-danger-dark has-text-white pl-2 pr-2"
	} else if percent <= 40 {
		fa_battery_string = "fa-battery-quarter"
		icon_color = "has-text-success pl-2 pr-2"
	} else if percent <= 60 {
		fa_battery_string = "fa-battery-half"
		icon_color = "has-text-success pl-2 pr-2"
	} else if percent <= 80 {
		fa_battery_string = "fa-battery-three-quarters"
		icon_color = "has-text-success pl-2 pr-2"
	} else {
		fa_battery_string = "fa-battery-full"
		icon_color = "has-text-success pl-2 pr-2"
	}

	return "<span class=\"" + icon_color + "\"><i class=\"fas " + fa_battery_string + "\"></i>&nbsp;" + strconv.Itoa(percent) + " &percnt;</span>"
}

func CalcDaysUntil(mydate string) int {
	var days int
	layout := "02.01.2006"
	t, err := time.Parse(layout, mydate)

	if err != nil {
		days = 0
	}
	days = int(t.Sub(time.Now().Truncate(24*time.Hour)).Hours() / 24)

	return days
}

func getLastMetrics(deveui string) OneMetric {
	var res OneMetric

	url := getenv("INFLUX_URL", "http://localhost:8086/api/v2/query?org=wobischorg")

	data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") 
                                 |> range(start:-365d) 
                                 |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
                                 |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
                                 |> filter(fn: (r) => r.lat != 0)
                                 |> sort(columns: ["_time"], desc: false)
                                 |> last(column: "_time")`, deveui))

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
	if err != nil {
		log.Fatal("Error reading request. ", err)
	}

	// Set headers
	req.Header.Set("Authorization", "Token "+INFLUX_RO_TOKEN)
	req.Header.Set("accept", "application/csv")
	req.Header.Set("content-type", "application/vnd.flux")

	// Set client timeout
	client := &http.Client{Timeout: time.Second * 10}

	// Send request
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("Error reading response. ", err)
	}
	defer resp.Body.Close()

	fmt.Println("response Status:", resp.Status)
	fmt.Println("response Headers:", resp.Header)

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal("Error reading body. ", err)
	}
	fmt.Println("response Body 2:", string(body))

	scanner := bufio.NewScanner(strings.NewReader(string(body)))
	location, err := time.LoadLocation("Europe/Zurich")
	for scanner.Scan() {
		s := strings.Split(scanner.Text(), ",")
		fmt.Printf("BlaBla: %v, elements: %d\n", s, len(s))
		if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) {
			t, err := time.Parse(time.RFC3339, s[5])
			if err != nil {
				fmt.Printf("error converting time: %s\n", s[5])
				continue
			}

			res.Fw = s[len(s)-4]
			res.Timestamp = t.In(location).Format("02.01.2006 15:04")
			if s[9] != "0" {
				res.Lat = s[len(s)-3]
				res.PosTimestamp = t.In(location).Format("02.01.2006 15:04")
			}
			if s[10] != "0" {
				res.Lon = s[len(s)-2]
				res.PosTimestamp = t.In(location).Format("02.01.2006 15:04")
			}
		}

	}

	data2 := []byte(fmt.Sprintf(`from(bucket:"wobischbucket")
                                 |> range(start:-365d)
                                 |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
                                 |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
                                 |> sort(columns: ["_time"], desc: false)
                                 |> last(column: "_time")`, deveui))

	req2, err := http.NewRequest("POST", url, bytes.NewBuffer(data2))
	if err != nil {
		log.Fatal("Error reading request. ", err)
	}

	// Set headers
	req2.Header.Set("Authorization", "Token "+INFLUX_RO_TOKEN)
	req2.Header.Set("accept", "application/csv")
	req2.Header.Set("content-type", "application/vnd.flux")

	// Send request
	resp2, err := client.Do(req2)
	if err != nil {
		log.Fatal("Error reading response. ", err)
	}
	defer resp2.Body.Close()

	fmt.Println("response Status:", resp2.Status)
	fmt.Println("response Headers:", resp2.Header)

	body2, err := ioutil.ReadAll(resp2.Body)
	if err != nil {
		log.Fatal("Error reading body. ", err)
	}
	fmt.Println("response Body 2:", string(body2))

	scanner2 := bufio.NewScanner(strings.NewReader(string(body2)))
	for scanner2.Scan() {
		s := strings.Split(scanner2.Text(), ",")
		fmt.Printf("BlaBla: %v, elements: %d\n", s, len(s))
		if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) {
			res.BatteryPercent = strconv.Itoa(vbat2percent(s[len(s)-1]))
			res.BatteryPercentHTML = template.HTML(vbat2html(s[len(s)-1]))
		}

	}

	res.Deveui = deveui
	res.DeveuiJS = template.JS(deveui)
	res.LatJS = template.JS(res.Lat)
	res.LonJS = template.JS(res.Lon)

	res.Alias = getDevAlias(deveui)
	res.Smsalarmactive = getDevSmsalarmactive(deveui)
	res.Smsnumber = getDevSmsnumber(deveui)
	res.Emailalarmactive = getDevEmailalarmactive(deveui)
	res.Email = getDevEmail(deveui)
	res.Greenzone = getDevGreenzone(deveui)
	res.Readonly = false
	res.ActiveUntil = getActiveUntil(deveui)
	res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil)

	fmt.Printf("Last Metrics: %v\n", res)
	return res
}
