package v1

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"regexp"
	"strconv"
	"time"

	"github.com/pingcap-inc/pchub/pkg/log"
	"github.com/spf13/viper"

	"github.com/pingcap-inc/security/internal/alert-server/store"
	v1 "github.com/pingcap-inc/security/model/v1"
)

type FeilianAlertService struct {
	store store.Factory
}

type FeilianAlertSrv interface {
	GetToken(ctx context.Context) string
	GetEdlpEvents(ctx context.Context)
}

var _ FeilianAlertSrv = (*FeilianAlertService)(nil)

const (
	baseUrl    = "https://feilian.pingcap.cn:8443"
	tokenUrl   = "/api/open/v1/token"
	edlpEvents = "/api/open/v1/security/edlp/events/list"
)

func newFeilianAlert(srv *service) *FeilianAlertService {
	return &FeilianAlertService{store: srv.store}
}

func (fa *FeilianAlertService) GetToken(ctx context.Context) string {

	log.L(ctx).Info("GetToken Called.")

	body := map[string]string{
		"access_key_id":     viper.GetString("feilian.id"),
		"access_key_secret": viper.GetString("feilian.secret"),
	}

	marshalBogy, _ := json.Marshal(body)
	reqBody := bytes.NewBuffer(marshalBogy)

	req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, baseUrl+tokenUrl, reqBody)
	if err != nil {
		log.L(ctx).Errorf("Make request error: %v", err)

		return ""
	}

	req.Header.Set("Content-Type", "application/json;charset=utf-8")

	client := &http.Client{}
	response, err := client.Do(req)
	if err != nil {
		log.L(ctx).Errorf("GetToken error: %v", err)

		return ""
	}

	defer response.Body.Close()

	if response.StatusCode != 200 {
		log.L(ctx).Errorf("GetToken error: %v", response.Status)

		return ""
	}

	var result map[string]interface{}
	err = json.NewDecoder(response.Body).Decode(&result)

	if err != nil {
		log.L(ctx).Errorf("Decode response error: %v", err)

		return ""
	}

	if result["code"].(float64) != 0 {
		log.L(ctx).Errorf("GetToken error: %v", result["message"])

		return ""
	}

	log.L(ctx).Infof("GetToken success: %v", result["data"].(map[string]interface{})["access_token"].(string))

	return result["data"].(map[string]interface{})["access_token"].(string)
}

func (fa *FeilianAlertService) GetEdlpEvents(ctx context.Context) {
	log.L(ctx).Info("GetEdlpEvents Called.")

	token := fa.GetToken(ctx)

	if token == "" {
		log.L(ctx).Error("GetToken error.")

		return
	}

	client := &http.Client{}
	i := 0

	var totalRecords []map[string]interface{}
	for { // 获取最近一小时的时间戳
		now := time.Now()

		oneHourBefore := now.Add(-time.Hour)
		req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, baseUrl+edlpEvents, nil)
		params := req.URL.Query()
		params.Set("offset", strconv.Itoa(i*50))
		params.Set("limit", "50")
		params.Set("start", strconv.FormatInt(oneHourBefore.Unix(), 10))
		params.Set("end", strconv.FormatInt(now.Unix(), 10))
		req.URL.RawQuery = params.Encode()

		req.Header.Set("Content-Type", "application/json;charset=utf-8")
		req.Header.Set("Authorization", token)

		response, err := client.Do(req)
		if err != nil {

			log.L(ctx).Errorf("GetEdlpEvents error: %v", err)

			return
		}

		defer response.Body.Close()

		var result map[string]interface{}
		err = json.NewDecoder(response.Body).Decode(&result)

		if err != nil {
			log.L(ctx).Errorf("Decode response error: %v", err)

			return
		}

		if result["code"].(float64) != 0 {
			log.L(ctx).Errorf("GetEdlpEvents error: %v", result["message"])

			return
		}

		if result["data"].(map[string]interface{})["count"].(float64) == 0 {
			break
		}

		i++
		if result["data"].(map[string]interface{})["items"] == nil {
			break
		}

		tmp_record := result["data"].(map[string]interface{})["items"].([]interface{})

		for _, record := range tmp_record {
			totalRecords = append(totalRecords, record.(map[string]interface{}))
		}

		log.L(ctx).Infof("GetEdlpEvents success: %v", result["data"].(map[string]interface{})["events"])
	}

	for _, record := range totalRecords {
		if record["strategy_name"].(string) != "sec_default" {
			continue
		}

		qs := QuestionDesc{
			UserName:   record["user_info"].(map[string]interface{})["full_name"].(string),
			ActionDesc: record["action_desc"].(string),
			KeyInfo:    record["key_info"].(string),
		}

		err := fa.SendLarkAlert(ctx, qs)

		if err != nil {
			log.L(ctx).Errorf("SendLarkAlert %s error: %v ", record["full_name"].(string), err)
		}

		// save to db
		// err := fa.store.FeilianAlert().Create(ctx, v1.FeilianAlert{
		// 	Name:       qs.UserName,
		// 	ActionDesc: qs.ActionDesc,
		// 	KeyInfo:    "",
		// 	CreatedAt:  time.Now(),
		// })

		// if err != nil {
		// 	log.L(ctx).Errorf("Save to db error: %v ", err)
		// }

	}

	log.L(ctx).Infof("GetEdlpEvents success: %v", totalRecords)
}

type QuestionDesc struct {
	UserName   string `json:"user_name"`
	ActionDesc string `json:"action_desc"`
	KeyInfo    string `json:"key_info"`
}

func (fa *FeilianAlertService) SendLarkAlert(ctx context.Context, questionDesc QuestionDesc) error {
	log.L(ctx).Info("SendLarkAlert Called.")

	comSrv := newCommon()

	reqBody := LarkCreateApprovalBody{
		ApprovalCode: phishingApprovalCode,
	}

	reqBody.UserId = primaryNotificationLarkUserID
	reqBody.OpenId = primaryNotificationLarkOpenID

	principal := primaryNotificationLarkUserID
	assistMembers := []string{}
	problemMsg := "用户：" + questionDesc.UserName + " 触发了数据防泄漏策略：" + questionDesc.ActionDesc + "，关键信息：" + questionDesc.KeyInfo

	var resForm []FormItem
	resForm = append(resForm, FormItem{
		Id:    v1.SafeType,
		Type:  "radioV2",
		Value: v1.DataSafe,
	}, FormItem{
		Id:    v1.Assets,
		Type:  "radioV2",
		Value: v1.Logging,
	}, FormItem{
		Id:    v1.ProblemDescription,
		Type:  "textarea",
		Value: problemMsg,
	}, FormItem{
		Id:    v1.ImprovementSuggestion,
		Type:  "textarea",
		Value: "到飞连后端查看具体日志判断是否有数据泄漏风险",
	}, FormItem{
		Id:    v1.EmergencyLevel,
		Type:  "radioV2",
		Value: v1.Normal,
	}, FormItem{
		Id:    v1.ResponsiblePerson,
		Type:  "contact",
		Value: []string{principal},
	}, FormItem{
		Id:    v1.Collaborators,
		Type:  "contact",
		Value: assistMembers,
	}, FormItem{
		Id:    v1.RectificationTime,
		Type:  "date",
		Value: time.Now().Add(time.Hour * 24 * 15).Format(time.RFC3339),
	})

	jsonForm, _ := json.Marshal(resForm)
	reqBody.Form = string(jsonForm)

	marshalBody, _ := json.Marshal(reqBody)

	larkToken, err := comSrv.GetLarkToken(ctx, 1)
	if err != nil {
		log.L(ctx).Errorf("GetLarkToken error: %v", err)

		return err
	}

	approvalR, err := comSrv.GetDataByReqLarkAPI(ctx, "POST", LarkOpenAPIBase+"/approval/v4/instances", bytes.NewReader(marshalBody), nil, larkToken)
	if err != nil {
		log.L(ctx).Errorf("error when submit lark approval, err: %s", err.Error())

		return err
	}

	var approvalRes ApprovalLarkResData
	err = json.Unmarshal(approvalR, &approvalRes)
	if err != nil {
		log.L(ctx).Errorf("error when unmarshal lark approval response, err: %s", err.Error())

		return err
	}

	log.L(ctx).Infof("submit lark approval success, approval id: %s", approvalRes.InstanceCode)

	return nil
}

func removeEmoji(content string) string {
	re := regexp.MustCompile(`[\x{1F600}-\x{1F6FF}|[\x{2600}-\x{26FF}|[\x{2700}-\x{27BF}]`)
	return re.ReplaceAllLiteralString(content, "")
}
