package main

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

type OneMetric struct {
	Deveui               string
	Alias                string
	Readonly             bool
	Alarmactive          string
	Smsnumber            string
	Minmax               string
	Timestamp            string
	Temperature          string
	Humidity             string
	Weight               string
	Weight_kg            string
	Pressure             string
	BatteryPercent       string
	ActiveUntil          string
	DaysUntilDeactivated int // berechneter Wert
}

var INFLUX_RO_TOKEN = os.Getenv("INFLUX_RO_TOKEN")

// metrics handler

func validProperty(prop string) bool {
	valid_properties := [...]string{"w", "t", "h", "p", "vp"}
	for _, p := range valid_properties {
		if p == prop {
			return true
		}
	}
	return false
}

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

		property, ok := request.URL.Query()["property"]
		if !ok || len(property[0]) < 1 {
			log.Println("Url Param 'property' is missing")
			fmt.Fprintf(response, "{ \"msg\": \"error: property must be specified in URL\" }")
			return
		}

		if !validProperty(property[0]) {
			log.Println("Url Param 'property' is invalid")
			fmt.Fprintf(response, "{ \"msg\": \"error: invalid property\" }")
			return
		}

		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=minibeieliorg")
		data := []byte(fmt.Sprintf(`from(bucket:"minibeielibucket") |> range(start: %s, stop: %s) |> filter(fn: (r) => r._measurement == "measurement") |> filter(fn: (r) => r._field == "%s") |> filter(fn: (r) => r.deveui == "%s")`, mystart, mystop, property[0], 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.Fprintf(response, "[\n")
		scanner := bufio.NewScanner(strings.NewReader(string(body)))
		first := true
		for scanner.Scan() {
			s := strings.Split(scanner.Text(), ",")
			if (len(s) >= 7) && !(strings.HasPrefix(s[5], "_")) {
				t, err := time.Parse(time.RFC3339, s[5])
				if err != nil {
					continue
				}
				a := t.Unix()
				b := s[6]
				if !(first) {
					fmt.Fprintf(response, ",")
				} else {
					first = false
				}
				fmt.Fprintf(response, "[%d000,%s]\n", a, b)
			}
		}
		fmt.Fprintf(response, "]\n")

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

func lastmetricsHandler(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\": \"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\": \"specified deveui does not belong to this user\" }")
			return
		}

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

		url := getenv("INFLUX_URL", "http://localhost:8086/api/v2/query?org=minibeieliorg")
		//data := []byte(fmt.Sprintf(`from(bucket:"minibeielibucket") |> range(start:-365d) |> filter(fn: (r) => r.deveui == "%s") |> filter(fn: (r) => r._field == "v" or r._field == "t") |> last() |> yield(name: "last")`,mydeveui))
		data := []byte(fmt.Sprintf(`from(bucket:"minibeielibucket") 
                                         |> range(start:-365d) 
                                         |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s") 
                                         |> filter(fn: (r) => r._field == "t" or r._field == "h" or r._field == "w" or r._field == "p" or r._field == "vp") 
                                         |> last() |> yield(name: "last")`, 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)))
		ts := ""
		t := ""
		h := ""
		w := ""
		p := ""
		vp := ""
		location, err := time.LoadLocation("Europe/Zurich")
		for scanner.Scan() {
			s := strings.Split(scanner.Text(), ",")
			if (len(s) >= 7) && !(strings.HasPrefix(s[5], "_")) {
				mytime, err := time.Parse(time.RFC3339, s[5])
				if err != nil {
					continue
				}
				ts = mytime.In(location).Format("02.01.2006 15:04")
				value := s[6]
				field := s[7]
				if field == "t" {
					t = value
				} else if field == "h" {
					h = value
				} else if field == "w" {
					w = value
				} else if field == "p" {
					p = value
				} else if field == "vp" {
					vp = value
				}
			}
		}
		fmt.Fprintf(response, `{ 
  "ts": "%s",
  "t": "%s",
  "h": "%s",
  "w": "%s",
  "p": "%s",
  "vp": "%s"
}`, ts, t, h, w, p, vp)

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

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=minibeieliorg")
	data := []byte(fmt.Sprintf(`from(bucket:"minibeielibucket") 
                                 |> range(start:-365d) 
                                 |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
                                 |> filter(fn: (r) => r._field == "t" or r._field == "h" or r._field == "w" or r._field == "p" or r._field == "vp") 
                                 |> last(column: "_time")
                                 |> yield(name: "last")`, 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:", string(body))

	scanner := bufio.NewScanner(strings.NewReader(string(body)))
	location, err := time.LoadLocation("Europe/Zurich")
	for scanner.Scan() {
		s := strings.Split(scanner.Text(), ",")
		if (len(s) >= 7) && !(strings.HasPrefix(s[5], "_")) {
			mytime, err := time.Parse(time.RFC3339, s[5])
			if err != nil {
				continue
			}
			res.Timestamp = mytime.In(location).Format("02.01.2006 15:04")
			value := s[6]
			field := s[7]
			if field == "t" {
				res.Temperature = value
			} else if field == "h" {
				res.Humidity = value
			} else if field == "w" {
				res.Weight = value
				i, err := strconv.Atoi(value)
				if err == nil {
					res.Weight_kg = fmt.Sprintf("%.3f", float64(i)/1000.0)
				} else {
					res.Weight_kg = "ERR"
				}
			} else if field == "p" {
				res.Pressure = value
			} else if field == "vp" {
				res.BatteryPercent = value
			}
		}
	}
	res.Deveui = deveui
	res.Alias = getDevAlias(deveui)
	res.Readonly = false
	res.Alarmactive = getDevAlarmactive(deveui)
	res.Smsnumber = getDevSmsnumber(deveui)
	res.Minmax = getDevMinmax(deveui)
	res.ActiveUntil = getActiveUntil(deveui)
	res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil)
	return res
}
