package wecom

import (
	"context"
	"time"

	"github.com/bytedance/sonic"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
)

// DirectoryMember 是企微 Adapter 交给 Application 的最小成员快照。
type DirectoryMember struct {
	UserID        string
	Name          string
	DepartmentIDs []int64
}

// DirectoryProvider 定义拉取应用可见成员的外部端口。
type DirectoryProvider interface {
	ListVisibleMembers(ctx context.Context, applicationID uint) ([]DirectoryMember, error)
}

// MemberRepository 定义可见成员快照同步和分页查询边界。
type MemberRepository interface {
	ReplaceVisible(ctx context.Context, applicationID uint, members []model.WeComMember, syncedAt time.Time) error
	ListVisible(ctx context.Context, applicationID uint, page, pageSize int, keyword string) ([]model.WeComMember, int64, error)
}

// DirectoryService 同步并分页查询企业微信应用可见成员。
type DirectoryService struct {
	applications interface {
		GetEnabled(ctx context.Context, applicationID uint) (*model.WeComApplication, error)
	}
	provider DirectoryProvider
	members  MemberRepository
	now      func() time.Time
}

// NewDirectoryService 创建企业微信通讯录同步用例。
func NewDirectoryService(applications interface {
	GetEnabled(ctx context.Context, applicationID uint) (*model.WeComApplication, error)
}, provider DirectoryProvider, members MemberRepository) *DirectoryService {
	return &DirectoryService{applications: applications, provider: provider, members: members, now: time.Now}
}

// Sync 拉取并替换指定应用当前可见成员快照。
func (s *DirectoryService) Sync(ctx context.Context, applicationID uint) (*dto.WeComMemberSyncResponse, error) {
	if s == nil || s.applications == nil || s.provider == nil || s.members == nil || applicationID == 0 {
		return nil, errors.New(errors.CodeServiceUnavailable, "企业微信通讯录服务未配置")
	}
	if !canManageWeComDirectory(ctx) {
		return nil, errors.New(errors.CodeForbidden)
	}
	application, err := s.applications.GetEnabled(ctx, applicationID)
	if err != nil {
		return nil, err
	}
	remoteMembers, err := s.provider.ListVisibleMembers(ctx, applicationID)
	if err != nil {
		return nil, err
	}
	syncedAt := s.now().UTC()
	members := make([]model.WeComMember, 0, len(remoteMembers))
	for _, member := range remoteMembers {
		departments, err := sonic.Marshal(member.DepartmentIDs)
		if err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "序列化企业微信成员部门失败")
		}
		members = append(members, model.WeComMember{
			ApplicationID: applicationID, CorpID: application.CorpID, UserID: member.UserID,
			Name: member.Name, DepartmentIDs: departments, Visible: true, SyncedAt: syncedAt,
			CreatedAt: syncedAt, UpdatedAt: syncedAt,
		})
	}
	if err := s.members.ReplaceVisible(ctx, applicationID, members, syncedAt); err != nil {
		return nil, err
	}
	return &dto.WeComMemberSyncResponse{ApplicationID: applicationID, SyncedCount: len(members), SyncedAt: syncedAt}, nil
}

// List 分页返回本地最近一次同步的应用可见成员。
func (s *DirectoryService) List(ctx context.Context, applicationID uint, request dto.WeComMemberListRequest) (*dto.WeComMemberListResponse, error) {
	if s == nil || s.applications == nil || s.members == nil || applicationID == 0 {
		return nil, errors.New(errors.CodeServiceUnavailable, "企业微信通讯录服务未配置")
	}
	if !canManageWeComDirectory(ctx) {
		return nil, errors.New(errors.CodeForbidden)
	}
	if request.Page <= 0 {
		request.Page = constants.DefaultPage
	}
	if request.PageSize <= 0 {
		request.PageSize = constants.DefaultPageSize
	}
	if request.PageSize > constants.MaxPageSize {
		return nil, errors.New(errors.CodeInvalidParam)
	}
	if _, err := s.applications.GetEnabled(ctx, applicationID); err != nil {
		return nil, err
	}
	members, total, err := s.members.ListVisible(ctx, applicationID, request.Page, request.PageSize, request.Keyword)
	if err != nil {
		return nil, err
	}
	items := make([]dto.WeComMemberResponse, 0, len(members))
	for _, member := range members {
		var departmentIDs []int64
		if len(member.DepartmentIDs) > 0 {
			if err := sonic.Unmarshal(member.DepartmentIDs, &departmentIDs); err != nil {
				return nil, errors.Wrap(errors.CodeInternalError, err, "解析企业微信成员部门失败")
			}
		}
		items = append(items, dto.WeComMemberResponse{
			ApplicationID: member.ApplicationID, CorpID: member.CorpID, UserID: member.UserID,
			Name: member.Name, DepartmentIDs: departmentIDs, SyncedAt: member.SyncedAt,
		})
	}
	return &dto.WeComMemberListResponse{Items: items, Total: total, Page: request.Page, PageSize: request.PageSize}, nil
}

func canManageWeComDirectory(ctx context.Context) bool {
	userType := middleware.GetUserTypeFromContext(ctx)
	return userType == constants.UserTypeSuperAdmin || userType == constants.UserTypePlatform
}
