package authing

import (
	"context"
	"encoding/json"
	"strconv"
	"sync"

	"github.com/Authing/authing-go-sdk/lib/enum"
	"github.com/Authing/authing-go-sdk/lib/management"
	authingmodel "github.com/Authing/authing-go-sdk/lib/model"
	"github.com/spf13/viper"

	"github.com/pingcap-inc/it-automation/pkg/log"
)

const (
	authingHost = "https://api.authing.pingcap.net"
)

type Authing interface {
	SetClient()
	GetUsers(ctx context.Context) ([]authingmodel.User, error)
	GetDepartmentHRBPEmail(mainDepartment *authingmodel.Node) string
	GetMainDepartmentByUser(userId string) *authingmodel.Node
	ExportAll(ctx context.Context) []authingmodel.OrgNode
	GetDeptById(ctx context.Context, id string) (*authingmodel.OrgNodeChildStr, error)
}

var _ Authing = (*authingService)(nil)

type authingService struct {
	client *management.Client
	mutex  sync.Mutex
}

func NewAuthingService() Authing {
	return &authingService{}
}

func (as *authingService) SetClient() {
	client, err := management.NewClientWithError(viper.Get("authing.id").(string), viper.Get("authing.secret").(string), authingHost)
	if err != nil {
		log.Errorf("Error when calling `NewClientWithError`: %s", err.Error())
		return
	}
	as.client = client
}

func (as *authingService) GetUsers(ctx context.Context) ([]authingmodel.User, error) {
	if as.client == nil {
		as.SetClient()
	}
	users := make([]authingmodel.User, 0)
	page := 1
	for {
		ok := true
		paginatedUsers, err := as.client.GetUserList(authingmodel.QueryListRequest{
			Page:           page,
			Limit:          200,
			SortBy:         enum.SortByCreatedAtAsc,
			WithCustomData: &ok,
		})
		if err != nil {
			log.L(ctx).Errorf("Error when calling `GetUserList`: %s", err.Error())

			return nil, err
		}
		if paginatedUsers == nil {
			break
		}
		if paginatedUsers.TotalCount == 0 || len(paginatedUsers.List) == 0 {
			break
		}
		users = append(users, paginatedUsers.List...)
		page++
	}
	return users, nil
}

func (as *authingService) GetDepartmentHRBPEmail(mainDepartment *authingmodel.Node) string {
	if as.client == nil {
		as.SetClient()
	}
	description := struct {
		HRBP int `json:"hRBP"`
	}{}
	if mainDepartment == nil || mainDepartment.Description == nil {
		return ""
	}
	err := json.Unmarshal([]byte(*mainDepartment.Description), &description)
	if err != nil {
		log.Errorf("Error when calling `json.Unmarshal`: %s", err.Error())

		return ""
	}

	return strconv.Itoa(description.HRBP)
	// beisenService := beisen.NewBeisenService()
	// usersInfo := beisenService.GetUsersInfo([]int{description.HRBP})
	// if usersInfo == nil || usersInfo.Data == nil || len(usersInfo.Data) == 0 {
	// 	log.Warnf("Can't find HRBP:", mainDepartment.Name)
	//
	// 	return ""
	// }
	// return usersInfo.Data[0].Email
}

func (as *authingService) GetMainDepartmentByUser(userId string) *authingmodel.Node {
	if as.client == nil {
		as.SetClient()
	}
	request := authingmodel.GetUserDepartmentsRequest{
		Id: userId,
	}
	departments, err := as.client.GetUserDepartments(request)
	if err != nil {
		log.Errorf("Error when calling `GetUserDepartments`: %s", err.Error())

		return nil
	}
	for _, department := range departments.List {
		if department.IsMainDepartment {
			return &department.Department
		}
	}
	return nil
}

func (as *authingService) ExportAll(ctx context.Context) []authingmodel.OrgNode {
	if as.client == nil {
		as.SetClient()
	}

	all, err := as.client.ExportAll()
	if err != nil {
		log.L(ctx).Errorf("Error when calling `ExportAll`: %s", err.Error())
		return nil
	}

	return all
}

func (as *authingService) GetDeptById(ctx context.Context, id string) (*authingmodel.OrgNodeChildStr, error) {
	if as.client == nil {
		as.SetClient()
	}

	node, err := as.client.GetOrgNodeById(id)
	if err != nil {
		log.L(ctx).Errorf("Error when calling `GetOrgNodeById`: %s", err.Error())

		return nil, err
	}

	return node, nil
}
