package v1

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/pingcap-inc/pchub/pkg/log"
	directory "google.golang.org/api/admin/directory/v1"
	alertcenter "google.golang.org/api/alertcenter/v1beta1"
	"google.golang.org/api/googleapi"

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

type gmailLockService struct {
	store store.Factory
}

type GmailLockSrv interface {
	RetrieveEvent(ctx context.Context) error
}

var _ GmailLockSrv = (*gmailLockService)(nil)

func newGmailLock(srv *service) *gmailLockService {
	return &gmailLockService{store: srv.store}
}

const (
	GmailLockApproval = "44F79E0D-5869-4F33-8070-DED6B1131AA5"

	LarkAprTaskNodeId   = "1ab4a465ecd1dc02e3152c4358a41f0e"
	LarkAprTaskNodeName = "责任人整改"
)

type GmailLockMsg struct {
	AlertId    string               `json:"alertId"`
	Data       googleapi.RawMessage `json:"data"`
	CreateTime string               `json:"createTime"`
}

type Data struct {
	Type  string `json:"@type"`
	Email string `json:"email"`
}

func (gas *gmailLockService) RetrieveEvent(ctx context.Context) error {
	log.L(ctx).Info("start to retrieve event")

	scope := []string{
		alertcenter.AppsAlertsScope,
	}

	alertcenterSrv, err := NewGoogleService().WsSrv(ctx, "alertcenter", scope...)
	if err != nil {
		log.L(ctx).Errorf("new google service error: %s", err.Error())

		return err
	}

	// check approval status
	err = gas.CheckApprStatus(ctx)
	if err != nil {
		log.L(ctx).Errorf("CheckApprStatus error: %v", err)

		return err
	}

	// over1h, err := checkOver1h(ctx)
	// if err != nil {
	// 	log.L(ctx).Errorf("checkOver1h error: %v", err)
	//
	// 	return err
	// }
	//
	// if !over1h {
	// 	log.L(ctx).Info("not over 1 hour")
	//
	// 	return nil
	// }

	// 获取当前时间
	now := time.Now()
	// 三天前的日期
	threeDaysAgo := now.AddDate(0, 0, -1)
	// 将日期设置为零点
	zeroTime := time.Date(threeDaysAgo.Year(), threeDaysAgo.Month(), threeDaysAgo.Day(), 0, 0, 0, 0, time.UTC)
	// 格式化日期
	inThreeDays := zeroTime.Format(time.RFC3339)

	allMessages := make([]GmailLockMsg, 0)
	alertcenterSrv.(*alertcenter.Service).Alerts.List().Filter("type:\"User suspended\" AND createTime >= \""+inThreeDays+"\"").Pages(ctx, func(page *alertcenter.ListAlertsResponse) error {
		if page == nil || page.Alerts == nil {
			return nil
		}
		for _, alert := range page.Alerts {
			allMessages = append(allMessages, GmailLockMsg{
				AlertId:    alert.AlertId,
				Data:       alert.Data,
				CreateTime: alert.CreateTime,
			})
		}

		return nil
	})

	if len(allMessages) == 0 {
		log.L(ctx).Info("no new alert")

		return nil
	}

	records, err := gas.store.GmailLock().List(ctx)
	recordMap := make(map[string]*v1.GmailLock)
	for _, record := range records {
		recordMap[record.AlertID] = record
	}

	notInDbMessages := make([]GmailLockMsg, 0)
	for _, msg := range allMessages {
		if _, ok := recordMap[msg.AlertId]; ok {
			continue
		}
		notInDbMessages = append(notInDbMessages, msg)
	}

	if len(notInDbMessages) == 0 {
		log.L(ctx).Info("no new alert")

		return nil
	}

	for _, msg := range notInDbMessages {
		err = gas.dealGmailLock(ctx, msg)
		if err != nil {
			return err
		}
	}

	log.L(ctx).Info("retrieve event success")

	return nil
}

func (gas *gmailLockService) dealGmailLock(ctx context.Context, msg GmailLockMsg) error {
	var dataTmp Data
	err := json.Unmarshal(msg.Data, &dataTmp)
	if err != nil {
		log.L(ctx).Errorf("unmarshal data error: %v", err)

		return err
	}

	apprCode, err := gas.SendLarkAlert(ctx, dataTmp.Email)
	if err != nil {
		return err
	}
	saveDbMsg := &v1.GmailLock{
		CreatedAt:    time.Now(),
		AlertID:      msg.AlertId,
		Email:        dataTmp.Email,
		AlertTime:    msg.CreateTime,
		ApprovalCode: apprCode,
		Status:       0,
	}

	err = gas.store.GmailLock().Create(ctx, []*v1.GmailLock{saveDbMsg})
	if err != nil {
		log.L(ctx).Errorf("save gmail alert to db error: %v", err)

		return err
	}

	return nil
}

func (gas *gmailLockService) SendLarkAlert(ctx context.Context, user string) (string, error) {
	log.L(ctx).Info("SendLarkAlert Called.")

	comSrv := &commonService{store: gas.store}

	reqBody := LarkCreateApprovalBody{
		ApprovalCode: GmailLockApproval,
	}

	reqBody.UserId = defaultUserLarkID
	reqBody.OpenId = defaultUserOpLarkID

	downAlertSrv := &downloadAlertService{store: gas.store}
	userInfo, err := downAlertSrv.GetUserInfo(ctx, user)

	fmt.Println(userInfo)
	if err != nil {
		log.L(ctx).Errorf("GetUserInfo error: %v", err)

		return "", err
	}

	principal := []string{"e359137a", "966933ae", "7ga61921", userInfo.HRBP} // 冯帅 | 刘寅 | Zedan Liu | Man Zeng | HRBP
	assistMembers := []string{
		"9c7ca79f", // 朱晓鹏
	}
	problemMsg := "The user's account has been suspended:\n" + user
	suggestions := "Understand the reason for the suspension to unlock and restore usage."

	var resForm []FormItem
	resForm = append(resForm, FormItem{
		Id:    "widget16614938322000001",
		Type:  "radioV2",
		Value: "l7a2jgxo-jqbkukuvsva-9", // 办公安全
	}, FormItem{
		Id:    "widget16614939701650001",
		Type:  "radioV2",
		Value: "l9zacui1-0zzwnosj2puf-1", // 邮箱
	}, FormItem{
		Id:    "widget16487195633330001",
		Type:  "textarea",
		Value: problemMsg,
	}, FormItem{
		Id:    "widget16614941455820001",
		Type:  "textarea",
		Value: suggestions,
	}, FormItem{
		Id:    "widget16487195406530001",
		Type:  "radioV2",
		Value: "l1et1ne6-wlhbvc5yoj-0", // 一般
	}, FormItem{
		Id:    "widget16614941687660001",
		Type:  "contact",
		Value: principal,
	}, FormItem{
		Id:    "widget16614942163000001",
		Type:  "contact",
		Value: assistMembers,
	}, FormItem{
		Id:    "widget16614942960990001",
		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, pchub)
	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 approvalRes.InstanceCode, nil
}

type approvalLarkResData struct {
	Status   string        `json:"status"`
	TaskList []LarkAprTask `json:"task_list"`
}

type LarkAprTask struct {
	Status   string `json:"status"`
	NodeId   string `json:"node_id"`
	NodeName string `json:"node_name"`
	Type     string `json:"type"`
}

func (gas *gmailLockService) CheckApprStatus(ctx context.Context) error {
	log.L(ctx).Info("CheckApprStatus Called.")

	records, err := gas.store.GmailLock().List(ctx)
	if err != nil {
		log.L(ctx).Errorf("Get gmail lock records error: %v", err)

		return err
	}

	comSrv := &commonService{store: gas.store}

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

		return err
	}

	for _, rd := range records {
		if rd.Status == 1 {
			continue
		}

		approvalR, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+"/approval/v4/instances/"+rd.ApprovalCode, nil, nil, larkToken)
		if err != nil {
			log.L(ctx).Errorf("error when get lark approval, err: %s", err.Error())

			continue
		}

		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())

			continue
		}

		if approvalRes.Status == "APPROVED" || checkTask(approvalRes.TaskList) {
			err := gas.SetUserActive(ctx, rd.Email)
			if err != nil {
				log.L(ctx).Errorf("SetUserActive error: %v", err)

				continue
			}

			rd.Status = 1
			err = gas.store.GmailLock().Update(ctx, rd)
			if err != nil {
				log.L(ctx).Errorf("update gmail lock record error: %v", err)

				continue
			}
		}
	}

	log.L(ctx).Info("CheckApprStatus success")

	return nil
}

func checkTask(taskList []LarkAprTask) bool {
	for _, task := range taskList {
		if task.NodeId == LarkAprTaskNodeId && task.NodeName == LarkAprTaskNodeName && task.Status == "DONE" {
			return true
		}
	}

	return false
}

func (gas *gmailLockService) SetUserActive(ctx context.Context, user string) error {
	log.L(ctx).Info("SetUserActive Called.")

	scope := []string{
		directory.AdminDirectoryUserScope,
	}

	wsInterface, err := NewGoogleService().WsSrv(ctx, "ak", scope...)
	if err != nil {
		log.L(ctx).Errorf("new google service error: %s", err.Error())

		return err
	}

	ak := wsInterface.(string)
	err = updateUserSuspendedStatus(user, false, ak)
	if err != nil {
		log.L(ctx).Errorf("updateUserSuspendedStatus error: %v", err.Error())

		return err
	}

	log.L(ctx).Infof("Set user %s active success", user)

	return nil
}

type UserUpdate struct {
	Suspended bool `json:"suspended"`
}

func updateUserSuspendedStatus(userEmail string, suspended bool, accessToken string) error {
	url := "https://admin.googleapis.com/admin/directory/v1/users/" + userEmail

	userUpdate := UserUpdate{Suspended: suspended}
	jsonData, err := json.Marshal(userUpdate)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to update user: %s", resp.Status)
	}

	return nil
}

var lastTime = 0

func checkOver1h(ctx context.Context) (bool, error) {
	log.L(ctx).Info("checkOver1h Called.")

	// 获取当前时间戳
	now := time.Now().Unix()

	defer func() {
		lastTime = int(now)
	}()

	if lastTime == 0 {
		log.L(ctx).Info("first time")

		lastTime = int(now)
		return false, nil
	}

	// 判断是否超过1小时
	if int(now)-lastTime > 3600 {
		log.L(ctx).Info("over 1 hour")

		return true, nil
	}

	log.L(ctx).Info("not over 1 hour")

	return false, nil
}
