package main

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

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

// 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 := "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._field == "lon" or r._field == "lat" or r._field == "vbat") 
                                         |> filter(fn: (r) => r.deveui == "%s")
                                         |> 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 TQvQxxLLAj1kTKWuEqcx7BA-KfE6WtJUeDlPa_Dnvms6Zqf6uh6lMbpXtzcsCjKO_x3PrpxxGDR5E6YnDB5PFg==")
		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\n", s)
			if (len(s) >= 11) && !(strings.HasPrefix(s[3], "_")) {
				t, err := time.Parse(time.RFC3339, s[3])
				if err != nil {
					fmt.Printf("error converting time: %s\n", s[3])
					continue
				}
				a := t.Unix()
				lat = string2float64(s[8])
				lon = string2float64(s[9])
				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[8], s[9], s[10], vbat2percent(s[10]))

				}

			}
		}
		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 vbat2percent(vbat string) int {
	res := 0
	i, err := strconv.Atoi(vbat)
	if err == nil {
		if i < 3400 {
			res = 1
		} else if i < 3700 {
			res = 2
		} else if i < 3850 {
			res = 3
		} else if i < 4000 {
			res = 4
		} else {
			res = 5
		}

	}

	return res
}

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()).Hours() / 24)

	return days
}

func getLastMetrics(deveui string) OneMetric {
	var res OneMetric

	url := "http://localhost:8086/api/v2/query?org=wobischorg"
	data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") 
                                 |> range(start:-365d) 
                                 |> tail(n:10)
                                 |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
                                 |> filter(fn: (r) => r._field == "lon" or r._field == "lat" or r._field == "vbat" or r._field == "fw") 
                                 |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")`, 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 TQvQxxLLAj1kTKWuEqcx7BA-KfE6WtJUeDlPa_Dnvms6Zqf6uh6lMbpXtzcsCjKO_x3PrpxxGDR5E6YnDB5PFg==")
	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\n", s)
		if (len(s) >= 12) && !(strings.HasPrefix(s[3], "_")) {
			t, err := time.Parse(time.RFC3339, s[3])
			if err != nil {
				fmt.Printf("error converting time: %s\n", s[3])
				continue
			}

			res.Fw = s[8]
			res.Timestamp = t.In(location).Format("02.01.2006 15:04")
			if s[9] != "0" {
				res.Lat = s[9]
				res.PosTimestamp = t.In(location).Format("02.01.2006 15:04")
			}
			if s[10] != "0" {
				res.Lon = s[10]
				res.PosTimestamp = t.In(location).Format("02.01.2006 15:04")
			}
			res.BatteryPercent = s[11]

		}

	}

	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.Readonly = false
	res.ActiveUntil = getActiveUntil(deveui)
	res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil)

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