package v1

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

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

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

type ExternalUserService struct {
	store store.Factory
}

type ExternalUserSrv interface {
	Retrieve(ctx context.Context)
}

var _ ExternalUserSrv = (*ExternalUserService)(nil)

func newExternalUser(srv *service) *ExternalUserService {
	return &ExternalUserService{store: srv.store}
}

func (eu *ExternalUserService) Retrieve(ctx context.Context) {
	log.L(ctx).Info("Retrieve Called.")

	eventLogs, err := eu.FetchEvent(ctx)
	if err != nil {
		log.L(ctx).Errorf("FetchEvent error: %v", err)

		return
	}

	for _, eventLog := range eventLogs {
		err = eu.JudgeEvent(ctx, eventLog)
		if err != nil {
			log.L(ctx).Errorf("JudgeEvent error: %v", err)
		}
	}

	log.L(ctx).Info("Retrieve end.")
}

type EventLog struct {
	CommonDrawers CommonDrawers `json:"common_drawers"`
	Objects       []EventObject `json:"objects"`
	OperatorValue string        `json:"operator_value"`
	EventTime     int64         `json:"event_time"`
	UniqueID      string        `json:"unique_id"`
}

type DrawerInfoList struct {
	InfoKey string `json:"info_key"`
	InfoVal string `json:"info_val"`
}

type CommonDrawers struct {
	CommonDrawInfoList []DrawerInfoList `json:"common_draw_info_list"`
}

type EventObject struct {
	ObjectName string `json:"object_name"`
}

type DrawerInfo struct {
	Key     string `json:"key"`
	KeyName string `json:"keyName"`
	Value   string `json:"value"`
}

type user struct {
	Name string `json:"name"`
}

func (eu *ExternalUserService) FetchEvent(ctx context.Context) ([]EventLog, error) {
	log.L(ctx).Info("FetchEvent Called.")
	now := time.Now()
	// 计算1小时之前的时间
	oneHourAgo := now.Add(-time.Hour)
	// 获取1小时之前的时间戳（以秒为单位）
	timestamp := oneHourAgo.Unix()

	params := make(map[string]string)
	params["oldest"] = strconv.FormatInt(timestamp, 10)
	params["user_id_type"] = "user_id"
	params["event_name"] = "im_addtochat"
	params["page_size"] = "200"

	eventLogs, err := EventRequest(ctx, params)

	return eventLogs, err
}

func EventRequest(ctx context.Context, params map[string]string) ([]EventLog, error) {
	log.L(ctx).Info("FetchEvent Called.")

	comSrv := newCommon()

	tok, _ := comSrv.GetLarkToken(ctx, 1)
	now := time.Now()
	// 计算1小时之前的时间
	oneHourAgo := now.Add(-time.Hour)
	// 获取1小时之前的时间戳（以秒为单位）
	timestamp := oneHourAgo.Unix()
	fmt.Println(timestamp)

	var pageToken string

	eventLogs := make([]EventLog, 0)
	for {
		if pageToken != "" {
			params["page_token"] = pageToken
		}
		data, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+"/admin/v1/audit_infos", nil, params, tok)
		if err != nil {
			log.L(ctx).Errorf("get data by req lark api failed:%s", err.Error())

			return nil, err
		}

		var eventLog []EventLog
		var dataMap map[string]interface{}

		err = json.Unmarshal(data, &dataMap)
		if err != nil {
			log.L(ctx).Errorf("unmarshal data failed:%s", err.Error())

			return nil, err
		}

		pageToken = dataMap["page_token"].(string)

		mar, _ := json.Marshal(dataMap["items"])
		err = json.Unmarshal(mar, &eventLog)
		if err != nil {
			log.L(ctx).Errorf("unmarshal data failed:%s", err.Error())

			return nil, err
		}

		eventLogs = append(eventLogs, eventLog...)

		hasMore := dataMap["has_more"].(bool)
		if !hasMore {
			break
		}
	}

	log.L(ctx).Infof("event logs:%v", eventLogs)

	return eventLogs, nil
}

func (eu *ExternalUserService) JudgeEvent(ctx context.Context, eventLog EventLog) error {
	log.L(ctx).Infof("JudgeEvent Called:%s.", eventLog.OperatorValue)

	comSrv := newCommon()

	nLarkTok, _ := comSrv.GetLarkToken(ctx, 1)

	var invUser string
	for _, cmlist := range eventLog.CommonDrawers.CommonDrawInfoList {
		if cmlist.InfoKey == "im_aduserlist" {
			invUser = cmlist.InfoVal
		}
	}

	if len(invUser) > 9 {
		log.L(ctx).Infof("invUser:%s to long", invUser)

		return nil
	}

	whiteList := []string{
		"dd17de8a", // 卜征
	}

	// 判断是否在白名单中
	for _, v := range whiteList {
		if v == invUser {
			log.L(ctx).Infof("invUser:%s in whiteList", invUser)

			return nil
		}
	}

	params := make(map[string]string)
	params["user_id_type"] = "user_id"

	_, err1 := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+"/contact/v3/users/"+invUser, nil, params, nLarkTok)

	oLarkTok, _ := comSrv.GetLarkToken(ctx, 2)

	_, err2 := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+"/contact/v3/users/"+invUser, nil, params, oLarkTok)

	if err1 != nil && err2 != nil {
		log.L(ctx).Infof("invUser:%s not internal user", invUser)

		opInfo, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+"/contact/v3/users/"+eventLog.OperatorValue, nil, params, nLarkTok)
		if err != nil {
			log.L(ctx).Errorf("get opInfo failed:%s", err.Error())

			return err
		}

		var opInfoMap map[string]interface{}
		json.Unmarshal(opInfo, &opInfoMap)

		var u user
		marshal, _ := json.Marshal(opInfoMap["user"])
		json.Unmarshal(marshal, &u)

		SecApprMsg := SecApprMsg{
			SafeType:      v1.DataSafe,
			Assets:        v1.Logging,
			PromDesc:      "操作者:" + u.Name + "拉入外部用户到群聊:" + eventLog.Objects[0].ObjectName,
			Suggestion:    "判断这个外部用户是不是风险用户以及群是否敏感",
			EmerLevel:     v1.Normal,
			RespPerson:    []string{primaryNotificationLarkUserID},
			Collaborators: []string{},
		}

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

			return err
		}
	}

	log.L(ctx).Infof("invUser:%s check end", invUser)

	return nil
}

type SecApprMsg struct {
	SafeType      string   `json:"safe_type"`
	Assets        string   `json:"assets"`
	PromDesc      string   `json:"prom_msg"`
	Suggestion    string   `json:"suggestion"`
	EmerLevel     string   `json:"emergency_level"`
	RespPerson    []string `json:"responsible_person"`
	Collaborators []string `json:"collaborators"`
}

func SendLarkAlert(ctx context.Context, apprMsg SecApprMsg) error {
	log.L(ctx).Info("SendLarkAlert Called.")

	comSrv := newCommon()

	reqBody := LarkCreateApprovalBody{
		ApprovalCode: phishingApprovalCode,
	}

	reqBody.UserId = primaryNotificationLarkUserID
	reqBody.OpenId = primaryNotificationLarkOpenID

	var resForm []FormItem
	resForm = append(resForm, FormItem{
		Id:    v1.SafeType,
		Type:  "radioV2",
		Value: apprMsg.SafeType,
	}, FormItem{
		Id:    v1.Assets,
		Type:  "radioV2",
		Value: apprMsg.Assets,
	}, FormItem{
		Id:    v1.ProblemDescription,
		Type:  "textarea",
		Value: apprMsg.PromDesc,
	}, FormItem{
		Id:    v1.ImprovementSuggestion,
		Type:  "textarea",
		Value: apprMsg.Suggestion,
	}, FormItem{
		Id:    v1.EmergencyLevel,
		Type:  "radioV2",
		Value: apprMsg.EmerLevel,
	}, FormItem{
		Id:    v1.ResponsiblePerson,
		Type:  "contact",
		Value: apprMsg.RespPerson,
	}, FormItem{
		Id:    v1.Collaborators,
		Type:  "contact",
		Value: apprMsg.Collaborators,
	}, 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
}
