package service

import (
	"context"
	"strings"
	"sync"

	authingmodel "github.com/Authing/authing-go-sdk/lib/model"
	admin "google.golang.org/api/admin/directory/v1"

	"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"
	"github.com/pingcap-inc/it-automation/pkg/log"
	"github.com/pingcap-inc/it-automation/pkg/provider/authing"
)

type LarkFieldsSrv interface {
	LarkFields(ctx context.Context)
}

type larkFieldsService struct {
	store store.Factory
}

var _ LarkFieldsSrv = (*larkFieldsService)(nil)

func newLarkFieldsService(srv *service) *larkFieldsService {
	return &larkFieldsService{
		store: srv.store,
	}
}

func (l *larkFieldsService) LarkFields(ctx context.Context) {
	log.L(ctx).Info("update lark user start")

	authingService := authing.NewAuthingService()
	larkService := lark.NewLarkService()

	log.L(ctx).Info("update lark user start")
	users, err := authingService.GetUsers(ctx)
	if err != nil {
		log.Errorf("Error when get users:%v", err)

		return
	}
	if users == nil || len(users) == 0 {
		log.Errorf("No user found")
		return
	}

	usersInLark, err := l.store.User().ListUsers(ctx)
	if err != nil {
		log.Errorf("Error when get all users from db:%v", err)

		return
	}

	larkIdMap, exMap := makeLarkUserIdMap(usersInLark)

	wg := &sync.WaitGroup{}
	sem := make(chan int, 10)

	for _, each := range users {
		if *each.IsDeleted || *each.Blocked {
			continue
		}
		wg.Add(1)
		go func(user authingmodel.User) {
			defer func() { <-sem }()
			defer wg.Done()
			sem <- 1
			l.perJob(ctx, larkService, authingService, larkIdMap, exMap, user)
		}(each)
	}

	wg.Wait()

	log.L(ctx).Info("lark fields update finish")
}

func (l *larkFieldsService) perJob(ctx context.Context, larkService lark.Lark, authingService authing.Authing, larkUserMap map[string]model.LarkUser, exMap map[string]model.LarkUser, user authingmodel.User) {

	mainDepartment := authingService.GetMainDepartmentByUser(user.Id)
	if mainDepartment != nil && mainDepartment.Name == "平凯（北京）" {
		return
	}

	hrbpExID := authingService.GetDepartmentHRBPEmail(mainDepartment)
	hrbpEmail := exMap[hrbpExID].Email
	hrbpLark, ok := larkUserMap[hrbpEmail]

	larkUser, ok := larkUserMap[*user.Email]
	if !ok {
		log.Errorf("Error when get lark user id %s", *user.Email)

		return
	}
	var extraMail string
	for _, customItem := range user.CustomData {
		if customItem.Key == "extraEmail" {
			extraMail = *customItem.Value
			break
		}
	}

	if extraMail != larkUser.ExtraMail {
		if extraMail != larkUser.ExtraMail && extraMail != "" {
			err := checkGoogleAlias(ctx, *user.Email, extraMail)
			if err != nil {
				log.Errorf("Error when checkGoogleAlias %s", err, extraMail)

				return
			}
		}
		larkService.UpdateUser(ctx, hrbpLark, extraMail, larkUser)
		userUpdate := larkUser
		if ok && hrbpEmail != "" {
			userUpdate.Hrbp = hrbpLark.LarkUserID
		}
		userUpdate.ExtraMail = extraMail
		l.updateDbUser(ctx, userUpdate)
	} else {
		log.Infof("user %s not need update extra mail:%s", *user.Email, extraMail)
	}
}

func (l *larkFieldsService) updateDbUser(ctx context.Context, user model.LarkUser) {
	err := l.store.User().SaveUser(&user)
	if err != nil {
		log.L(ctx).Errorf("Error when update user:%v", err)
	}
}

func makeLarkUserIdMap(users []*model.LarkUser) (map[string]model.LarkUser, map[string]model.LarkUser) {
	mailMap := make(map[string]model.LarkUser)
	for _, user := range users {
		mailMap[user.Email] = *user
	}

	exMap := make(map[string]model.LarkUser)
	for _, user := range users {
		if user.ItalentID == "" {
			continue
		}
		exMap[user.ItalentID] = *user
	}

	return mailMap, exMap
}

func checkGoogleAlias(ctx context.Context, priMail, aliasMail string) error {
	log.L(ctx).Infof("checkGoogleAlias priMail:%s, aliasMail:%s", priMail, aliasMail)

	if priMail == aliasMail {
		log.L(ctx).Infof("priMail == aliasMail")

		return nil
	}

	googleSrv, err := NewGoogleService(ctx)
	if err != nil {
		log.L(ctx).Errorf("Error when NewGoogleService:%v", err)

		return err
	}

	resp, err := googleSrv.Users.Get(priMail).Do()
	if err != nil {
		log.L(ctx).Errorf("Error when get user:%v", err)

		return err
	}

	for _, each := range resp.Aliases {
		if each == aliasMail {
			return nil
		}
	}

	_, err = googleSrv.Users.Aliases.Insert(priMail, &admin.Alias{Alias: aliasMail}).Do()
	if err != nil {
		if strings.Contains(err.Error(), "already exists") {
			log.L(ctx).Infof("alias already exists:%s", aliasMail)

			return nil
		}
		log.L(ctx).Errorf("Error when insert alias:%v", err)

		return err
	}

	log.L(ctx).Infof("insert alias success")

	return nil
}
