package main

import (
	"encoding/json"
	"escalation/prom/db"
	"escalation/prom/model"
	"escalation/prom/write"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"

	. "github.com/prometheus/prometheus/prompb"
	"github.com/spf13/viper"
)

var client http.Client

// help functions to receive HTTP response.
func SendAndHandleHTTPRes[T model.HttpRes](req *http.Request, struct_res *T) {
	res, err := client.Do(req)
	if err != nil {
		log.Println("req err, ", err)
	}

	defer res.Body.Close() //修改信息
	res_body, _ := ioutil.ReadAll(res.Body)

	err2 := json.Unmarshal(res_body, struct_res)
	if err2 != nil {
		log.Println("error:", err2)
	}
}

// init the yaml file.
func setUpConfig() {
	viper.SetConfigName("conf")     // name of config file (without extension)
	viper.SetConfigType("yaml")     // REQUIRED if the config file does not have the extension in the name
	viper.AddConfigPath("./config") // optionally look for config in the working directory
	err := viper.ReadInConfig()     // Find and read the config file
	if err != nil {                 // Handle errors reading the config file
		panic(fmt.Errorf("Fatal error config file: %w \n", err))
	}

	client = http.Client{
		Timeout: 600 * time.Second,
	}
}

// init the log.
func initLog(file_name string) {
	file := "./log/" + file_name
	logFile, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
	if err != nil {
		panic(err)
	}
	log.SetOutput(logFile)
	log.SetPrefix("[Prometheus Data Fetching]")
	log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)
	return
}

const THANOS_URL = `https://thanos-query.oauth.prod.aws.tidbcloud.com/api/v1`

func toPromTIme(origin string) string {
	split_slice := strings.Split(origin, " ")
	return split_slice[0] + "T" + split_slice[1] + ".000Z"
}

// func GetOneClusterOneMetric(file *os.File, metric string, clusterId string, cookie string, created string, finish string) {
// 	loc, _ := time.LoadLocation("UTC")
// 	create_ts, err_created := time.ParseInLocation("2006-01-02 15:04:05", created, loc)
// 	if err_created != nil {
// 		panic(err_created)
// 	}
// 	finish_ts, err_finish := time.ParseInLocation("2006-01-02 15:04:05", finish, loc)
// 	if err_finish != nil {
// 		panic(err_finish)
// 	}
// 	start_ts := create_ts.Unix()
// 	log.Println("handling cluster: ", clusterId)
// 	// res := make([]WriteRequest, 0)
// 	for start_ts < finish_ts.Unix() {
// 		var end_ts int64
// 		if start_ts+900 < finish_ts.Unix() {
// 			end_ts = start_ts + 900 // add one month
// 		} else {
// 			end_ts = start_ts + 900
// 		}

// 		SendOneMetric(clusterId, metric, toPromTIme(time.Unix(start_ts, 0).Format("2006-01-02 15:04:05")), toPromTIme(time.Unix(end_ts, 0).Format("2006-01-02 15:04:05")), cookie)

// 		// if len(wr.Timeseries) > 0 {
// 		// 	// write.SendWriteRequest(wr)
// 		// log.Println("write request: ", len(wr.Timeseries))
// 		// 	time.Sleep(time.Duration(2) * time.Second)
// 		// }

// 		// res = append(res, *wr)
// 		start_ts += 900
// 	}

// }

func ConstJsonStringMap(tagName string) string {
	jsonMap := make(map[string]string)
	jsonMap["MetricName"] = "__name__"
	jsonMap["Cluster"] = "cluster"
	jsonMap["ClusterId"] = "cluster_id"
	jsonMap["Component"] = "component"
	jsonMap["Instance"] = "instance"
	jsonMap["Job"] = "job"
	jsonMap["KubernetesNamespace"] = "kubernetes_namespace"
	jsonMap["Name"] = "name"
	jsonMap["Project"] = "project"
	jsonMap["Receive"] = "receive"
	jsonMap["SeedProvider"] = "seed_provider"
	jsonMap["SeedRegion"] = "seed_region"
	jsonMap["ShootInfra"] = "shoot_infra"
	jsonMap["ShootName"] = "shoot_name"
	jsonMap["Tenant"] = "tenant"
	jsonMap["TenantId"] = "tenant_id"
	jsonMap["TidbCluster"] = "tidb_cluster"
	jsonMap["Type"] = "type"
	jsonMap["Db"] = "db"
	return jsonMap[tagName]
}

func main() {

	setUpConfig()
	initLog("fetching.log")

	file := "./data/data"
	_, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
	if err != nil {
		panic(err)
	}

	startTime := "2022-11-02 19:25:00"
	endTime := "2022-11-08 00:10:00"

	clusters := db.GetThanosTenantAndCluster()

	FetchAndWrite([]string{`tidb_executor_statement_total`, `tikv_engine_size_bytes`}, startTime, endTime, clusters, `_gcl_au=1.1.1426016273.1666253791; _fbp=fb.1.1666253791596.156696553; __adroll_fpc=6a51c77aa53a1551596ea4904644e33e-1666253791756; _ga=GA1.2.1924045205.1666253791; mp_eb52fe7ba2189cde179831aa368962ea_mixpanel={"distinct_id": "183f47720e71047-086f4f324fdb2f-19525635-16a7f0-183f47720e8a79","$device_id": "183f47720e71047-086f4f324fdb2f-19525635-16a7f0-183f47720e8a79","$initial_referrer": "$direct","$initial_referring_domain": "$direct","$search_engine": "google"}; _hjSessionUser_2436003=eyJpZCI6IjVkMWQ4YzljLTkwNzgtNTYxYS04YmZkLWJmNzFlZmRiZjgwMiIsImNyZWF0ZWQiOjE2NjYyNTM3OTE3MDcsImV4aXN0aW5nIjp0cnVlfQ==; mp_24683377b1976f477ca1db59603e5999_mixpanel={"distinct_id": "183f4771f2e18ef-0e2d0b719f5252-19525635-16a7f0-183f4771f2f16e2","$device_id": "183f4771f2e18ef-0e2d0b719f5252-19525635-16a7f0-183f4771f2f16e2","$initial_referrer": "$direct","$initial_referring_domain": "$direct","$search_engine": "google"}; __ar_v4=EAELTDRLTJASBH4WFZ44BQ:20221019:4|KOZMIWNYMJHZ5DAL4EF25B:20221019:4|5GUJULC3VBE6VM73JJH2PI:20221019:4; _ga_CPG2VW1Y41=GS1.1.1666253790.1.1.1666253838.0.0.0; _ga_9FRXHHPYVY=GS1.1.1666253790.1.1.1666253838.0.0.0; __zlcmid=1CXlJX45kDfx7do; _oauth2_proxy=6NEErMoXjRtJFskoLMaOwi6sLphLinsk8N8aQcLFvwLmrR8lOepE_q1Iw5ynDlYW0JuRRGnLfQqheidfkhMPVisKgznza_k5iBTBEXpbIWl0YEGDin7oYQ6KdB4eGwDjYEeOP6VjXX_pkogDPszM13nX-O-J0l1pvTK_NnwDxb_eOz622BC3Vh60-tl5z1Y_4Vh6iAa_sfGEeMds0tX1wVcKJ3y6egEgppfBxyxyAmykUdHt8QRcbF9tZazPxk2-6xZhi95s13wxH7GIgubh7e2zG7hs9dbWSMYpp_obcj9W3Y3N62EKY97CEBFDloqqiq8r7MxVRHAaosXQKJID2jAdzuVVwQOzCi3Epar3bN41SBxY4o_H69u9u3pxKKvOuqGCUcGoiGTr1177W9z4EaDuLOKON3tvSDffXYymClpBHkEHGxPOmab_366kvVUOpy73qYA9xNRmZP4GY3xQ9mWwIxiqPJJTauPN4ci1RZWUClyr-hq_SLbwBI9fxUNl8sylr9K2t0CVaGqg1DfLnaE7KPQi7zN-32rMRIGqt9G0R31TNT343eoqlpGvqynzHZj37CG9Zo0xhr25HagUJohvsEkBYttqQNxhwCTpAbS41s7MOmVsOX4gCM8bVi4ifncNvmHtMBtQ4Wiv7zq5dAx-268wg2Uv36W5LFSHl9Q15p4nkk6s5Ox3TWiaru3U0-bKYsp7TyP-YK2_70hFUjMx45X7BuO1XtM4n4evpWK1TobXLFkmhb0XdxIZP0X65oaPQ_MuD03McPY5Co4nvBZNCTJSbUiYD2dkDi5Krgf30lSt3MZEIiMzFfb-KfRB62z_Twjhf8kSIaCtzaU1orDicr29F3GDIMStXJOgE4PEbpYNrCNvif8c_Cvj2mPEMT5JB4vQztF7nKDYs5n8bzAzHcD2JdOalRmg4YGa_H3AEi5WN4L5jhwDRpkSnxDRkYGUUNMEQNZsPBj3PN5xg3uP1EP53PdQ7Qc_GSn5gsLb6QZvhdwX0D1Ring25edjR9h8BOjeJ_8eJSOtzh_iOQ5g4nRLhdy1SICcM_VQoXCNTau4jg5NzUxUiNeYcYzuaVk67yFmuNfS9C7SqR32xUwB5wLc5eMYziv-3t1gzKTnT8JKBcYkjjU76x4tYBORRTINF4WY126h-2OZgXXEASYz8FEgbuBCjJmdDMn9LUGsbLVXxYGu3hVVfeqoL-vNAkvz-epV_7lsVkQ0_kXQxJJAsfHYECuWcF4TDg2eWrCGg8tNakV3DqyZUjmodhQL4Zc91ZidzAZ05XIrS3lPiKtEVbWVeOrHzLh0h0F3-cwujuJd6drunw0mrguavFJvf5ejYMsePELPRf4rzrsg-pBn96RCyc7MxPo9mDIIk9VMB8df2vINwxU7WCpIrAyzBKiYdFlRK3SSyZdv75Mwe9866hKWEPNPZoMP-Ah18I2RYvUeCoPFL08__i2QLb0bSoBULDvHcMFQTjLrz7BvTddJF_BmRWs_o5DOXTadPHwxizmZ0XRKAQOAHQoAhdvQu6YGRmPQfc4JpG_v_CQNthgoXre_LohrV8j6jZ_AC0ZNbw4rtNH-Q_g_kMc1iUXdLuqgxCB3hba522u72FfnigVHg8tGsq6oL5g9nvNbrPYz2Pz7XUwl8QkYn3DapFgCs-pAjJW9cPxusbU=|1667369213|sH5dBUU0jnyI6-TBs2Xg0bw61-PgYScfvnKalhfTPIQ=`)

	log.Println("len: ", len(clusters))

}

func FetchAndWrite(metrics []string, startTime string, endTime string, clusters []db.ThanosTenantCluster, cookie string) {
	loc, _ := time.LoadLocation("UTC")
	create_time, err_created := time.ParseInLocation("2006-01-02 15:04:05", startTime, loc)
	if err_created != nil {
		panic(err_created)
	}
	finish_time, err_finish := time.ParseInLocation("2006-01-02 15:04:05", endTime, loc)
	if err_finish != nil {
		panic(err_finish)
	}
	start_ts := create_time.Unix()
	for start_ts < finish_time.Unix() {
		var end_ts int64
		if start_ts+899 < finish_time.Unix() {
			end_ts = start_ts + 899 // add 15 min
		} else {
			end_ts = finish_time.Unix()
		}
		// write 15m's metrics of all clusters.
		wrs := make([]*WriteRequest, 0)
		log.Println("start time: ", time.Unix(start_ts, 0).Format("2006-01-02 15:04:05"))
		log.Println("end time: ", time.Unix(end_ts, 0).Format("2006-01-02 15:04:05"))
		for _, cluster := range clusters {
			// fmt.Println("handling cluster: ", cluster.ClusterId)
			clusterId := cluster.ClusterId
			for _, metric := range metrics {
				oneClusterRequest := GetOneMetricRequest(metric, clusterId, toPromTIme(time.Unix(start_ts, 0).Format("2006-01-02 15:04:05")), toPromTIme(time.Unix(end_ts, 0).Format("2006-01-02 15:04:05")), cookie)
				wrs = append(wrs, oneClusterRequest...)
			}
		}
		sort.Slice(wrs, func(i, j int) bool {
			return wrs[i].Timeseries[0].Samples[0].Timestamp < wrs[j].Timeseries[0].Samples[0].Timestamp
		})
		log.Println("total number of entities is:, ", len(wrs))
		WriteBatchToProm(wrs)
		start_ts += 900
	}
}

func GetOneMetricRequest(metric string, clusterId string, start string, end string, cookie string) []*WriteRequest {
	params := url.Values{}
	params.Add(`query`, metric+`{cluster_id=~".*`+clusterId+`"}`)
	params.Add(`start`, start)
	params.Add(`end`, end)
	params.Add(`step`, `15m`)

	req, _ := http.NewRequest("GET", `https://thanos-query.oauth.prod.aws.tidbcloud.com/api/v1/query_range?`+params.Encode(), nil)
	req.Header.Add("Cookie", cookie)
	var struct_res model.ThanosResponse
	SendAndHandleHTTPRes(req, &struct_res)
	log.Println("response: ", len(struct_res.Data.Result))

	wrs := BuildWriteRequest(struct_res.Data.Result, start)
	return wrs
}

func BuildWriteRequest(resArr []model.ThanosResult, time string) []*WriteRequest {
	wrs := make([]*WriteRequest, 0)
	for _, res := range resArr {
		labels := make([]Label, 0)
		t := reflect.TypeOf(res.Metric)
		v := reflect.ValueOf(res.Metric)
		for k := 0; k < t.NumField(); k++ {
			if v.Field(k).String() != "" {
				labels = append(labels, Label{Name: ConstJsonStringMap(t.Field(k).Name), Value: v.Field(k).String()})
			}
		}
		for _, value := range res.Values {
			tss := make([]TimeSeries, 0)
			samples := make([]Sample, 0)
			ts := value[0].(float64)
			ts_int64 := int64(ts * 1000)
			val := value[1].(string)
			val_float64, _ := strconv.ParseFloat(val, 64)
			samples = append(samples, Sample{Timestamp: ts_int64, Value: val_float64})
			tss = append(tss, TimeSeries{Labels: labels, Samples: samples})
			wr := &WriteRequest{
				Timeseries: tss,
			}
			wrs = append(wrs, wr)
		}

	}
	return wrs
}

func WriteBatchToProm(wrs []*WriteRequest) {
	for _, wr := range wrs {
		// log.Println("len of ts is: ", len(wr.Timeseries))
		// log.Println("len of value is: ", len(wr.Timeseries[0].Samples))
		log.Println("ts is: ", wr.Timeseries[0].Samples[0].Timestamp)
		write.SendWriteRequest(wr)
	}
}
