//go:generate ${GOBIN}/mockgen -destination contact_mock.go -package=contact ${GO_MODULE}/pkg/infra/clients/larkcli/contact Contact
package contact

import (
	"context"
	"fmt"

	lark "github.com/larksuite/oapi-sdk-go/v3"
	larkcontact "github.com/larksuite/oapi-sdk-go/v3/service/contact/v3"
)

type Contact interface {
	BatchGetUsers(ctx context.Context, userIds []string) ([]*larkcontact.User, error)
	BetchGetUserIds(ctx context.Context, emails []string) ([]*larkcontact.UserContactInfo, error)
	GetUser(ctx context.Context, userId string) (*larkcontact.User, error)

	GetDepartment(ctx context.Context, departmentId string) (*larkcontact.Department, error)
	GetDepartmentChildren(ctx context.Context, departmentID string) ([]*larkcontact.Department, error)
}

func New(larkCli *lark.Client) Contact {
	return &client{
		larkCli: larkCli,
	}
}

type client struct {
	larkCli *lark.Client
}

func (c *client) BatchGetUsers(ctx context.Context, userIds []string) ([]*larkcontact.User, error) {
	users := make([]*larkcontact.User, 0, len(userIds))
	for i := 0; i < len(userIds); i = i + 50 {
		end := i + 50
		if end > len(userIds) {
			end = len(userIds)
		}
		req := larkcontact.NewBatchUserReqBuilder().
			UserIds(userIds[i:end]).
			UserIdType("user_id").
			DepartmentIdType("department_id").
			Build()
		resp, err := c.larkCli.Contact.User.Batch(ctx, req)
		if err != nil {
			return nil, fmt.Errorf("fail to batch get lark users, err: %w", err)
		}
		if !resp.Success() {
			return nil, fmt.Errorf("fail to batch get lark users, code: %d, message: %s", resp.Code, resp.Msg)
		}
		users = append(users, resp.Data.Items...)
	}
	return users, nil
}

func (c *client) BetchGetUserIds(ctx context.Context, emails []string) ([]*larkcontact.UserContactInfo, error) {
	userContactInfos := make([]*larkcontact.UserContactInfo, 0, len(emails))
	for i := 0; i < len(emails); i = i + 50 {
		end := i + 50
		if end > len(emails) {
			end = len(emails)
		}
		req := larkcontact.NewBatchGetIdUserReqBuilder().
			UserIdType("user_id").
			Body(larkcontact.NewBatchGetIdUserReqBodyBuilder().Emails(emails[i:end]).Build()).
			Build()
		resp, err := c.larkCli.Contact.User.BatchGetId(ctx, req)
		if err != nil {
			return nil, fmt.Errorf("fail to batch get lark user ids, err: %w", err)
		}
		if !resp.Success() {
			return nil, fmt.Errorf("fail to batch get lark user ids, code: %d, message: %s", resp.Code, resp.Msg)
		}
		userContactInfos = append(userContactInfos, resp.Data.UserList...)
	}
	return userContactInfos, nil
}

func (c *client) GetUser(ctx context.Context, userId string) (*larkcontact.User, error) {
	req := larkcontact.NewGetUserReqBuilder().
		UserId(userId).
		UserIdType("user_id").
		DepartmentIdType("department_id").
		Build()
	resp, err := c.larkCli.Contact.User.Get(context.Background(), req)
	if err != nil {
		return nil, fmt.Errorf("get user's information %s via lark api: %w", userId, err)
	}
	if !resp.Success() {
		return nil, fmt.Errorf("get user's information %s via lark api, code: %d, msg: %s", userId, resp.Code, resp.Msg)
	}
	return resp.Data.User, nil
}

func (c *client) GetDepartment(ctx context.Context, departmentId string) (*larkcontact.Department, error) {
	req := larkcontact.NewGetDepartmentReqBuilder().
		DepartmentId(departmentId).
		DepartmentIdType("department_id").
		Build()
	resp, err := c.larkCli.Contact.Department.Get(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("fail to get department %s info: %v", departmentId, err)
	}
	if !resp.Success() {
		return nil, fmt.Errorf("fail to get department %s info, code: %d, message: %s", departmentId, resp.Code, resp.Msg)
	}
	return resp.Data.Department, nil
}

func (c *client) GetDepartmentChildren(ctx context.Context, departmentID string) ([]*larkcontact.Department, error) {
	nextToken := ""
	children := make([]*larkcontact.Department, 0)
	for {
		req := larkcontact.NewChildrenDepartmentReqBuilder().
			DepartmentId(departmentID).
			UserIdType("user_id").
			DepartmentIdType("department_id").
			FetchChild(true).
			PageToken(nextToken).
			PageSize(50).
			Build()
		resp, err := c.larkCli.Contact.Department.Children(context.Background(), req)
		if err != nil {
			return nil, fmt.Errorf("fail to get children department: %v", err)
		}
		if !resp.Success() {
			return nil, fmt.Errorf("fail to get children department, code: %d, message: %s", resp.Code, resp.Msg)
		}
		children = append(children, resp.Data.Items...)

		if resp.Data.HasMore != nil && *resp.Data.HasMore {
			nextToken = *resp.Data.PageToken
		} else {
			break
		}
	}

	return children, nil
}
