package service

import (
	"context"

	authingmodel "github.com/Authing/authing-go-sdk/lib/model"

	"github.com/pingcap-inc/it-automation/internal/sync-google-email/model"
	"github.com/pingcap-inc/it-automation/internal/sync-google-email/service/lark"
	"github.com/pingcap-inc/it-automation/internal/sync-google-email/store/mysql"
	"github.com/pingcap-inc/it-automation/pkg/log"
	"github.com/pingcap-inc/it-automation/pkg/provider/authing"
)

func (s *sgeService) SyncDeptUser() {
	log.Info("start sync authing and lark user")

	ctx := context.Background()

	err := lark.SyncLarkDepartment(ctx)
	if err != nil {
		log.L(ctx).Errorf("Error when calling `lark.SyncLarkDepartment`: %s", err.Error())

		return
	}

	err = lark.SyncUser(ctx)
	if err != nil {
		log.L(ctx).Errorf("Error when calling `lark.SyncUser`: %s", err.Error())

		return
	}

	authingService := authing.NewAuthingService()

	log.L(ctx).Info("start sync authing and lark user")
	// sync user
	authingUsers, err := authingService.GetUsers(ctx)
	if err != nil {
		log.L(ctx).Errorf("Error when calling `authingService.GetUsers`: %s", err.Error())

		return
	}
	mysqlStore, _ := mysql.GetMySQLFactoryOr(nil)
	usersDb, err := mysqlStore.User().ListUsers(ctx)
	if err != nil {
		log.L(ctx).Errorf("Error when calling `mysqlStore.User().ListUsers`: %s", err.Error())

		return
	}

	var usersDbMap = make(map[string]*model.LarkUser)

	for _, user := range usersDb {
		usersDbMap[user.Email] = user
	}

	var usersToSave []*model.LarkUser

	for _, user := range authingUsers {
		if user.Email == nil || *user.Email == "" {
			continue
		}
		if u, ok := usersDbMap[*user.Email]; ok {
			log.L(ctx).Debugf("user %s exist", *user.Email)
			u.AuthingId = user.Id
			if user.ExternalId != nil && *user.ExternalId != "" {
				u.ItalentID = *user.ExternalId
			}
			usersToSave = append(usersToSave, u)
		} else {
			log.L(ctx).Warnf("User %s not exists in db", *user.Email)
		}
	}

	if len(usersToSave) > 0 {
		err = mysqlStore.User().SaveUsers(usersToSave)
		if err != nil {
			log.L(ctx).Errorf("Error when calling `mysqlStore.User().SaveUsers`: %s", err.Error())
		}
	}

	log.L(ctx).Info("start sync authing user end")

	// sync department
	log.L(ctx).Info("start sync authing department")

	authingDepartments := authingService.ExportAll(ctx)
	if authingDepartments == nil {
		log.L(ctx).Errorf("Error when calling `authingService.ExportAll`")

		return
	}
	ADptMap := make(map[string]authingmodel.OrgNode)
	visMap := make(map[string]bool)
	for _, node := range authingDepartments {
		dfsDepartment(node, ADptMap, visMap)
	}
	departmentsDb, err := mysqlStore.AuthingDepartment().ListDepartments()
	if err != nil {
		log.L(ctx).Errorf("Error when calling `mysqlStore.Department().ListDepartments`: %s", err.Error())

		return
	}

	var dptUpdate []*model.AuthingDepartment
	var dptDelete []*model.AuthingDepartment
	var dptInsert []*model.AuthingDepartment
	for _, dptInDb := range departmentsDb {

		if dpt, ok := ADptMap[dptInDb.AuthingId]; ok {
			if dptInDb.Name != dpt.Name {
				dptInDb.Name = dpt.Name
				dptUpdate = append(dptUpdate, dptInDb)
			}
			visMap[dpt.Id] = true
		} else {
			dptDelete = append(dptDelete, dptInDb)
		}
	}

	for _, dpt := range ADptMap {
		if !visMap[dpt.Id] {
			dptInsert = append(dptInsert, &model.AuthingDepartment{
				AuthingId: dpt.Id,
				Name:      dpt.Name,
			})
		}
	}

	if len(dptUpdate) > 0 {
		err = mysqlStore.AuthingDepartment().SaveDepartments(dptUpdate)
		if err != nil {
			log.L(ctx).Errorf("Error when calling `mysqlStore.Department().SaveDepartments`: %s", err.Error())
		}
	}

	if len(dptDelete) > 0 {
		err = mysqlStore.AuthingDepartment().DelDepartments(dptDelete)
		if err != nil {
			log.L(ctx).Errorf("Error when calling `mysqlStore.Department().DeleteDepartments`: %s", err.Error())
		}
	}

	if len(dptInsert) > 0 {
		err = mysqlStore.AuthingDepartment().SaveDepartments(dptInsert)
		if err != nil {
			log.L(ctx).Errorf("Error when calling `mysqlStore.Department().InsertDepartments`: %s", err.Error())
		}
	}

	log.Info("end sync authing")
}

func dfsDepartment(node authingmodel.OrgNode, ADptMap map[string]authingmodel.OrgNode, visMap map[string]bool) {
	ADptMap[node.Id] = node
	visMap[node.Id] = false
	for _, child := range *node.Children {
		dfsDepartment(child, ADptMap, visMap)
	}
}
