package main

import (
	"bytes"
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/ddliu/go-httpclient"
	"github.com/tidbcloud/metering/pkg/common/config"
	"github.com/tidbcloud/metering/pkg/common/constants"
)

var serviceconfigpath string
var tolerance string
var feishuSecret string

func main() {
	flag.StringVar(&serviceconfigpath, "serviceconfig", "./configs/serviceconfig.yaml", "service info.")
	flag.StringVar(&tolerance, "tolerance", "0.001", "tolerance")

	flag.Parse()

	mgmt_dsn := os.Getenv("MGMT_DSN")
	feishuSecret = os.Getenv("FEISHU_SECRET")
	serviceConfig, err := config.LoadServiceConfig(serviceconfigpath)
	if err != nil {
		log.Panicf("load service config failed. %s", err.Error())
	}
	config.Init(serviceConfig)

	endTs := time.Now().Unix() / constants.ReconciliationRangeSeconds * constants.ReconciliationRangeSeconds

	startTs := endTs - constants.ReconciliationRangeSeconds
	toleranceFloat, err := strconv.ParseFloat(tolerance, 64)
	if err != nil {
		log.Panicf("invalid config: tolerance. %s", err.Error())
	}
	unmatchedClusters, detail, unmatchedClustersV2, detailV2, err := Compare(context.TODO(), startTs, endTs, toleranceFloat, mgmt_dsn)
	if err != nil {
		log.Panicf("compare failed. %s", err.Error())
	}

	SendMessage(unmatchedClusters, detail, endTs, false)
	SendMessage(unmatchedClustersV2, detailV2, endTs, true)
}

var feishuAPI = "https://open.feishu.cn/open-apis/bot/v2/hook/ebf5a113-0ceb-405c-a46e-6e684472717b"

func SendMessage(unmatchedClusters []string, detail string, endTs int64, V2 bool) {
	var title string

	if !V2 {
		if len(unmatchedClusters) == 0 {
			title = fmt.Sprintf("%s 计量对账通过, time range: %d - %d", config.GetConfig().Env, endTs-constants.ReconciliationRangeSeconds, endTs)
		} else {
			title = fmt.Sprintf("%s 计量对账异常, time range: %d - %d", config.GetConfig().Env, endTs-constants.ReconciliationRangeSeconds, endTs)
		}
	} else {
		if len(unmatchedClusters) == 0 {
			title = fmt.Sprintf("%s 计量新老SKU对账通过, time range: %d - %d", config.GetConfig().Env, endTs-constants.ReconciliationRangeSeconds, endTs)
		} else {
			title = fmt.Sprintf("%s 计量新老SKU对账异常, time range: %d - %d", config.GetConfig().Env, endTs-constants.ReconciliationRangeSeconds, endTs)
		}
	}
	signTS := time.Now().Unix()
	sign, err := GenSign(feishuSecret, signTS)
	if err != nil {
		log.Panicf("get sign failed. %s", err.Error())
	}
	if len(unmatchedClusters) > 0 {
		log.Println("get unmatchedClusters")
		log.Println(unmatchedClusters)
		log.Println(detail)
	}
	message := Message{
		MsgType:   "post",
		Timestamp: fmt.Sprintf("%d", signTS),
		Sign:      sign,
		Content: Content{
			Post: Post{
				ZHCN: ZHCN{
					Title: title,
					Content: [][]ContentItem{
						{
							{
								Tag:  "text",
								Text: fmt.Sprintf("unmatched clusters: %s", strings.Join(unmatchedClusters, ",")),
							},
							{
								Tag:  "text",
								Text: fmt.Sprintf("\n detail:\n %s", detail),
							},
						},
					},
				},
			},
		},
	}
	messageJson, err := json.Marshal(message)
	if err != nil {
		log.Panicf("Marshal failed. %s", err.Error())
	}
	log.Println(string(messageJson))
	send(message)
}

func send(message Message) {
	messageBody, err := json.Marshal(message)
	if err != nil {
		log.Panicf("marshal feishu message failed. %s", err.Error())
	}

	resp, err := httpclient.Do("POST", feishuAPI, map[string]string{
		"Content-Type": "application/json",
	}, bytes.NewBuffer(messageBody))

	log.Println(string(messageBody))

	if err != nil {
		log.Panicf("send feishu message failed. %s", err.Error())
	}
	if resp.StatusCode != http.StatusOK {
		respData, _ := io.ReadAll(resp.Body)
		log.Panicf("send feishu message failed. httpcode %d. %s", resp.StatusCode, string(respData))
	}
}

func GenSign(secret string, timestamp int64) (string, error) {
	stringToSign := fmt.Sprintf("%v", timestamp) + "\n" + secret
	var data []byte
	h := hmac.New(sha256.New, []byte(stringToSign))
	_, err := h.Write(data)
	if err != nil {
		return "", err
	}
	signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
	return signature, nil
}

type Message struct {
	MsgType   string  `json:"msg_type"`
	Content   Content `json:"content"`
	Timestamp string  `json:"timestamp"`
	Sign      string  `json:"sign"`
}

type Content struct {
	Post Post `json:"post"`
}
type Post struct {
	ZHCN ZHCN `json:"zh_cn"`
}
type ZHCN struct {
	Title   string          `json:"title"`
	Content [][]ContentItem `json:"content"`
}
type ContentItem struct {
	Tag  string `json:"tag"`
	Text string `json:"text"`
}
