package fetch

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

	"github.com/spf13/viper"
)

var clientIncrease *http.Client

func getIncreaseClient() *http.Client {
	if clientIncrease == nil {
		clientIncrease = &http.Client{Timeout: 600 * time.Second}
	}
	return clientIncrease
}

// help functions to receive HTTP response.
func SendAndHandleHTTPRes[T model.HttpRes](req *http.Request, struct_res *T) {
	req.Close = true
	res, err := getIncreaseClient().Do(req)
	if err != nil {
		log.Println("http request error: ", err)
		clientIncrease = &http.Client{Timeout: 600 * time.Second}
		SendAndHandleHTTPRes(req, struct_res)
		return
	}

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

	err2 := json.Unmarshal(res_body, struct_res)
	if err2 != nil {
		log.Println("error during unmarshal json:", err2)
	}
	valueBody := reflect.ValueOf(*struct_res)
	err_string := valueBody.FieldByName("Error").String()
	if err_string != "" {
		log.Println("error during deserialize http response " + req.RequestURI + ": " + err_string)
	}
}

const Auth0_URL = "https://tidb-soc2.us.auth0.com/oauth/token"
const Auth0_Cookie = "did=s%3Av0%3A84d51910-2374-11ed-b233-1d96ad033395.PyQZ7x6oLtc42kmSu7pRSeDJIluWg6J%2BjjGy%2BysrVtg; did_compat=s%3Av0%3A84d51910-2374-11ed-b233-1d96ad033395.PyQZ7x6oLtc42kmSu7pRSeDJIluWg6J%2BjjGy%2BysrVtg"

// Get the Auth0 Token.
func GetToken() string {
	postData := url.Values{}
	postData.Add("grant_type", "client_credentials")
	postData.Add("client_id", "Sd4aekPCa5nMUSQBIvf8eGSL895WtV4o")
	postData.Add("client_secret", "-SLP5UvxJdwGbVaKupxgv53T6otXguApPoG0fBsWCIqDG8_TuvlYnlkhPQHcWaKT")
	postData.Add("audience", "https://tidb-soc2.us.auth0.com/api/v2/")

	req, _ := http.NewRequest("POST", Auth0_URL, strings.NewReader(postData.Encode()))
	req.Header.Set("User-Agent", "curl")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Cookie", Auth0_Cookie)

	var struct_res model.AuthResponse
	SendAndHandleHTTPRes(req, &struct_res)
	return struct_res.Access_token
}

const External_URL = "https://clinic.pingcap.com/clinic/api/v1/orgs/external"

func GetOrgIdByTenant(tenant_id string) string {
	url := External_URL + "/" + tenant_id
	log.Println("org url is: " + url)
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Add("Authorization", "Bearer "+viper.GetString("token-clinic"))

	var struct_res model.ExternalResponse
	SendAndHandleHTTPRes(req, &struct_res)

	return struct_res.Id
}

const Clinic_URL = "https://clinic.pingcap.com/clinic/api/v1/orgs"

// get Region information via Clinic API.
func GetRegionInfo(tenant_id string, cluster_id string) string {
	url := Clinic_URL + "/" + tenant_id + "/clusters/" + cluster_id
	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer "+viper.GetString("token-clinic"))

	var struct_res model.ClinicResponse
	SendAndHandleHTTPRes(req, &struct_res)

	return struct_res.Region
}

const Cluster_Inf_URL = "https://www.gs.us-west-2.aws.observability.tidbcloud.com/api/v1/clusters"

// get cluster unique ID.
func GetClusterId(server_domain string, region string, biz_type string, application_id string, token string, vendor string) string {
	url := Cluster_Inf_URL + "/?vendor=" + vendor + "&region=" + region + "&bizType=" + biz_type + "&applicationID=" + application_id
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Add("Authorization", "Bearer "+token)

	var struct_res model.ClusterResponse
	SendAndHandleHTTPRes(req, &struct_res)

	// if there is no cluster, it means that this cluster is dev tier.
	if len(struct_res.Data.Clusters) == 0 {
		return ""
	}
	return struct_res.Data.Clusters[0].Id

}

func GetPromURL(server_domain string, cluster_id string) string {
	return "https://" + server_domain + "/internal/metrics/" + cluster_id
}

const limit = 20

func GetNeededClinicClusters(token string) []db.ClusterTenant {
	url := `https://clinic.pingcap.com/clinic/api/v1/dashboard/clusters2?query=&show_deleted=false&cluster_status=normal&deploy_type=dedicated&&tenant_plan=on_demand&tenant_plan=poc`
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Add("Authorization", "Bearer "+token)
	var structRes model.ClinicClusters
	SendAndHandleHTTPRes(req, &structRes)
	pageNum := structRes.Total / limit
	res := make([]db.ClusterTenant, 0)
	for i := 1; i < int(pageNum)+2; i++ {
		pageURL := url + `&page=` + strconv.Itoa(i) + `&limit=` + strconv.Itoa(limit)
		req, _ := http.NewRequest("GET", pageURL, nil)
		req.Header.Add("Authorization", "Bearer "+token)
		var structPageRes model.ClinicClusters
		SendAndHandleHTTPRes(req, &structPageRes)
		for _, v := range structPageRes.Items {
			res = append(res, db.ClusterTenant{Tenant: v.TenantID, Cluster: v.ClusterID})
		}
	}
	// append OSSInsight.
	res = append(res, db.ClusterTenant{Cluster: "1379661944632784068", Tenant: "1372813089187041280"})
	return res
}

func GetAllClusterInf(token_auth0 string) []model.ClusterInf {
	read_bytes, _ := ioutil.ReadFile("./persist/cluster_url.json")
	var clusterInfs []model.ClusterInf
	err := json.Unmarshal(read_bytes, &clusterInfs)
	if err != nil {
		log.Println("error during unmarshal cluster URLs: ", err)
	}

	// clusterMap := make(map[string]model.ClusterInf)
	aimClusters := make([]model.ClusterInf, 0)
	for _, ele := range GetNeededClinicClusters(viper.GetString("token-clinic")) {
		clusterInf := BeenPersisted(ele.Cluster, clusterInfs)
		if clusterInf != nil {
			aimClusters = append(aimClusters, *clusterInf)
			continue
		}
		log.Println("GET INFORMATION FOR: cluster is: " + ele.Cluster + " and tenant is " + ele.Tenant)
		// first, get the org's Id by tenant Id.
		org_id := GetOrgIdByTenant(ele.Tenant)
		log.Println("org id is: ", org_id)

		// then, get region's Id by org's Id and cluster Id.
		region := GetRegionInfo(org_id, ele.Cluster)
		log.Println("region is: ", region)

		server_domain := viper.GetString("domain." + region)
		log.Println("server domain is: ", server_domain)

		// aws or gcp.
		vendor := viper.GetString("vendor." + region)
		log.Println("vender is: ", vendor)

		// get the unique cluster id.
		id := GetClusterId(server_domain, region, "tidbcloud", ele.Cluster, token_auth0, vendor)

		// if id is empty, this cluster is dev tier, ignore,
		// else, use the unique cluster Id to get the prometheus query API.
		if id != "" {
			prom_url := GetPromURL(server_domain, id)
			clusterInfs = append(clusterInfs, model.ClusterInf{Cluster: ele.Cluster, Domain: server_domain, Region: region, Tenant: ele.Tenant, PromURL: prom_url, OrgId: org_id})
			aimClusters = append(aimClusters, model.ClusterInf{Cluster: ele.Cluster, Domain: server_domain, Region: region, Tenant: ele.Tenant, PromURL: prom_url, OrgId: org_id})
			log.Println("url for cluster " + ele.Cluster + " is:" + prom_url)
		} else {
			log.Println("cluster " + ele.Cluster + " is dev tier, ignore.")
		}
	}
	log.Println("length of aim: ", len(aimClusters))
	log.Println("length of persisted clusters: ", len(clusterInfs))

	// Persist the newest cluster information to disk.
	jsonBytes, _ := json.Marshal(clusterInfs)
	cluster_file, _ := os.Create("./persist/cluster_url.json")
	defer cluster_file.Close()
	cluster_file.Write(jsonBytes)
	return aimClusters
}

func BeenPersisted(cluster string, clusterInfs []model.ClusterInf) *model.ClusterInf {
	for _, ele := range clusterInfs {
		if ele.Cluster == cluster {
			return &ele
		}
	}
	return nil
}
