package com.starry.admin.modules.weichat.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.starry.admin.common.aspect.ClerkUserLogin;
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.clerk.enums.ClerkMediaReviewState;
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
import com.starry.admin.modules.clerk.module.entity.*;
import com.starry.admin.modules.clerk.module.vo.PlayClerkCommodityEditVo;
import com.starry.admin.modules.clerk.module.vo.PlayClerkCommodityQueryVo;
import com.starry.admin.modules.clerk.service.*;
import com.starry.admin.modules.clerk.service.impl.PlayClerkUserInfoServiceImpl;
import com.starry.admin.modules.clerk.service.impl.PlayClerkUserReviewInfoServiceImpl;
import com.starry.admin.modules.media.enums.MediaOwnerType;
import com.starry.admin.modules.media.service.IPlayMediaService;
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.module.vo.PlayOrderCompleteVo;
import com.starry.admin.modules.order.module.vo.PlayOrderEvaluateQueryVo;
import com.starry.admin.modules.order.module.vo.PlayOrderStateEditVo;
import com.starry.admin.modules.order.service.IPlayOrderEvaluateInfoService;
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
import com.starry.admin.modules.personnel.service.IPlayPersonnelAdminInfoService;
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
import com.starry.admin.modules.shop.module.entity.PlayGiftInfoEntity;
import com.starry.admin.modules.shop.service.IPlayGiftInfoService;
import com.starry.admin.modules.statistics.service.IPlayClerkPerformanceService;
import com.starry.admin.modules.weichat.entity.*;
import com.starry.admin.modules.weichat.entity.clerk.PlayClerkLevelAndRankingVo;
import com.starry.admin.modules.weichat.entity.clerk.PlayClerkLevelInfoReturnVo;
import com.starry.admin.modules.weichat.entity.clerk.PlayClerkUserInfoQueryVo;
import com.starry.admin.modules.weichat.entity.clerk.PlayClerkUserInfoResultVo;
import com.starry.admin.modules.weichat.entity.gift.PlayClerkGiftReturnVo;
import com.starry.admin.modules.weichat.entity.order.PlayClerkOrderDetailsReturnVo;
import com.starry.admin.modules.weichat.entity.order.PlayClerkOrderInfoQueryVo;
import com.starry.admin.modules.weichat.entity.order.PlayClerkOrderListReturnVo;
import com.starry.admin.modules.weichat.service.WxCustomMpService;
import com.starry.admin.modules.weichat.service.WxCustomUserService;
import com.starry.admin.utils.DateRangeUtils;
import com.starry.admin.utils.SecurityUtils;
import com.starry.admin.utils.SmsUtils;
import com.starry.common.redis.RedisCache;
import com.starry.common.result.R;
import com.starry.common.result.TypedR;
import com.starry.common.utils.ConvertUtil;
import com.starry.common.utils.StringUtils;
import com.starry.common.utils.VerificationCodeUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

/**
 * @author admin
 */
@Api(tags = "微信店员接口", description = "微信端店员相关接口")
@Slf4j
@RestController
@RequestMapping("/wx/clerk/")
public class WxClerkController {

    @Resource
    RedisCache redisCache;

    @Resource
    private PlayClerkUserInfoServiceImpl playClerkUserInfoService;

    @Resource
    private IPlayClerkLevelInfoService playClerkLevelInfoService;

    @Resource
    private IPlayClerkUserInfoService clerkUserInfoService;

    @Resource
    private IPlayClerkCommodityService playClerkCommodityService;

    @Resource
    private WxCustomUserService customUserService;

    @Resource
    private IPlayOrderInfoService playOrderInfoService;

    @Resource
    private IPlayOrderEvaluateInfoService playOrderEvaluateInfoService;

    @Resource
    private PlayClerkUserReviewInfoServiceImpl playClerkUserReviewInfoService;

    @Resource
    private IPlayClerkDataReviewInfoService playClerkDataReviewInfoService;

    @Resource
    private IPlayClerkClassificationInfoService playClerkClassificationInfoService;

    @Resource
    private IPlayGiftInfoService giftInfoService;

    @Resource
    private IPlayClerkPerformanceService playClerkPerformanceService;

    @Resource
    private IPlayPersonnelAdminInfoService playPersonnelAdminInfoService;

    @Resource
    private IPlayPersonnelGroupInfoService playPersonnelGroupInfoService;
    @Resource
    private SmsUtils smsUtils;
    @Resource
    private WxCustomMpService wxCustomMpService;
    @Resource
    private com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService clerkMediaAssetService;
    @Resource
    private IPlayMediaService mediaService;

    /**
     * 店员获取个人业绩信息
     */
    @ApiOperation(value = "查询店员业绩", notes = "店员获取个人业绩信息")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ClerkUserLogin
    @PostMapping("/user/queryPerformanceInfo")
    public R queryPerformanceInfo(
            @ApiParam(value = "业绩查询条件", required = true) @Validated @RequestBody PlayClerkWxPerformanceInfoQueryVo vo) {
        PlayClerkUserInfoEntity entity = clerkUserInfoService
                .selectById(ThreadLocalRequestDetail.getClerkUserInfo().getId());
        List<PlayClerkLevelInfoEntity> clerkLevelInfoEntity = playClerkLevelInfoService.selectAll();
        String startTime = DateRangeUtils.normalizeStartOptional(vo.getStartTime());
        String endTime = DateRangeUtils.normalizeEndOptional(vo.getEndTime());
        List<PlayOrderInfoEntity> orderInfoEntities = playOrderInfoService.clerkSelectOrderInfoList(entity.getId(),
                startTime, endTime);
        List<PlayPersonnelGroupInfoEntity> groupInfoEntities = playPersonnelGroupInfoService.selectAll();
        return R.ok(playClerkPerformanceService.getClerkPerformanceInfo(entity, orderInfoEntities, clerkLevelInfoEntity,
                groupInfoEntities, startTime, endTime));
    }

    /**
     * 店员获取个人等级信息
     */
    @ApiOperation(value = "查询店员等级", notes = "店员获取个人等级信息")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = PlayClerkLevelInfoReturnVo.class)})
    @ClerkUserLogin
    @GetMapping("/user/queryLevelInfo")
    public R queryLevelInfo() {
        PlayClerkUserInfoEntity entity = clerkUserInfoService
                .selectById(ThreadLocalRequestDetail.getClerkUserInfo().getId());
        PlayClerkLevelInfoEntity level = playClerkLevelInfoService.selectPlayClerkLevelInfoById(entity.getLevelId());
        PlayClerkLevelInfoReturnVo returnVo = new PlayClerkLevelInfoReturnVo();
        returnVo.setLevelName(level.getName());
        returnVo.setLevelId(level.getId());
        List<PlayClerkLevelAndRankingVo> levelAndRanking = new ArrayList<>();
        levelAndRanking.add(new PlayClerkLevelAndRankingVo("女神", 0, 0, 0));
        levelAndRanking.add(new PlayClerkLevelAndRankingVo("男神", 0, 0, 1));
        levelAndRanking.add(new PlayClerkLevelAndRankingVo("首席", 1, 2, 2));
        levelAndRanking.add(new PlayClerkLevelAndRankingVo("镇店", 3, 8, 3));
        levelAndRanking.add(new PlayClerkLevelAndRankingVo("金牌", 9, 18, 4));
        levelAndRanking.add(new PlayClerkLevelAndRankingVo("普通", 19, 150, 5));
        returnVo.setLevelAndRanking(levelAndRanking);
        return R.ok(returnVo);
    }

    /**
     * 店员获取个人信息
     */
    @ApiOperation(value = "查询店员信息", notes = "店员获取个人详细信息")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = PlayClerkUserInfoResultVo.class),
            @ApiResponse(code = 500, message = "用户不存在")})
    @ClerkUserLogin
    @GetMapping("/user/queryById")
    public R queryById() {
        PlayClerkUserInfoEntity entity = clerkUserInfoService
                .selectById(ThreadLocalRequestDetail.getClerkUserInfo().getId());
        if (entity == null) {
            throw new CustomException("用户不存在");
        }
        return R.ok(clerkUserInfoService.getVo(entity));
    }

    @ApiOperation(value = "发送验证码", notes = "店员绑定手机号发送验证码")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功", response = String.class)})
    @ClerkUserLogin
    @PostMapping("/user/sendCode")
    public R sendCode(
            @ApiParam(value = "发送验证码信息", required = true) @Validated @RequestBody PlayClerkUserSendCodeVo vo) {
        String codeKey = "login_codes:" + SecurityUtils.getTenantId() + "_"
                + SecureUtil.md5(vo.getAreaCode() + vo.getPhone());
        String code = VerificationCodeUtils.getVerificationCode(4);
        redisCache.setCacheObject(codeKey, code, 5L, TimeUnit.MINUTES);
        // 发送验证码,
        return R.ok(code);
    }

    @ApiOperation(value = "绑定手机号", notes = "店员绑定手机号")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功"), @ApiResponse(code = 500, message = "验证码错误")})
    @ClerkUserLogin
    @PostMapping("/user/bindCode")
    public R bindCode(
            @ApiParam(value = "绑定验证码信息", required = true) @Validated @RequestBody PlayClerkUserBindCodeVo vo) {
        String codeKey = "login_codes:" + SecurityUtils.getTenantId() + "_"
                + SecureUtil.md5(vo.getAreaCode() + vo.getPhone());
        String code = redisCache.getCacheObject(codeKey);
        if (code == null || !code.equals(vo.getCode())) {
            throw new CustomException("验证码错误");
        }
        // 账号绑定操作
        String id = ThreadLocalRequestDetail.getClerkUserInfo().getId();
        PlayClerkUserInfoEntity clerkUserInfoEntity = new PlayClerkUserInfoEntity();
        clerkUserInfoEntity.setId(id);
        clerkUserInfoEntity.setPhone(vo.getPhone());
        playClerkUserInfoService.update(clerkUserInfoEntity);
        // 删除验证码缓存
        redisCache.deleteObject(codeKey);

        return R.ok("成功");
    }

    @ApiOperation(value = "店员申请", notes = "用户申请成为店员")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功"), @ApiResponse(code = 500, message = "系统错误，用户不存在"),
            @ApiResponse(code = 500, message = "当前用户已经是店员"), @ApiResponse(code = 500, message = "已有申请未审核")})
    @ClerkUserLogin
    @PostMapping("/user/add")
    @Transactional(rollbackFor = Exception.class)
    public R userAdd(@ApiParam(value = "店员申请信息", required = true) @Validated @RequestBody PlayClerkUserByWxAddVo vo) {
        String clerkId = ThreadLocalRequestDetail.getClerkUserInfo().getId();

        PlayClerkUserInfoEntity userInfo = playClerkUserInfoService.selectById(clerkId);
        if (userInfo == null) {
            throw new CustomException("系统错误，用户不存在");
        }
        wxCustomMpService.checkSubscribeThrowsExp(userInfo.getOpenid(), userInfo.getTenantId());
        if ("1".equals(userInfo.getClerkState())) {
            throw new CustomException("当前用户已经是店员");
        }
        PlayClerkUserReviewInfoEntity entity = playClerkUserReviewInfoService.queryByClerkId(userInfo.getId(), "0");
        if (entity != null) {
            throw new CustomException("已有申请未审核");
        }
        if (StringUtils.isNotEmpty(vo.getPhone()) && StringUtils.isNotEmpty(vo.getSmsCode())) {
            smsUtils.checkSmsCode(vo.getPhone(), vo.getSmsCode());
        }

        entity = ConvertUtil.entityToVo(vo, PlayClerkUserReviewInfoEntity.class);
        entity.setReviewState("0");
        entity.setClerkId(clerkId);
        entity.setAddTime(LocalDateTime.now());
        playClerkUserReviewInfoService.create(entity);
        wxCustomMpService.sendClerkApplyNoticeForAdmin(entity, userInfo.getTenantId());
        return R.ok("申请成功");
    }

    @ApiOperation(value = "更新昵称", notes = "店员更新昵称")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ClerkUserLogin
    @PostMapping("/user/updateNickname")
    public R updateNickname(@ApiParam(value = "昵称信息", required = true) @Validated @RequestBody PlayClerkUserNicknameVo vo) {
        PlayClerkUserInfoEntity userInfo = ThreadLocalRequestDetail.getClerkUserInfo();
        PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
        entity.setClerkId(userInfo.getId());
        entity.setDataType("0");
        entity.setReviewState("0");
        entity.setDataContent(Collections.singletonList(vo.getNickname()));
        playClerkDataReviewInfoService.create(entity);
        return R.ok().message("提交成功，等待审核~");
    }

    @ApiOperation(value = "更新性别", notes = "店员更新性别")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ClerkUserLogin
    @PostMapping("/user/updateSex")
    public R updateSex(@ApiParam(value = "性别信息", required = true) @Validated @RequestBody PlayClerkUserSexVo vo) {
        PlayClerkUserInfoEntity userInfo = ThreadLocalRequestDetail.getClerkUserInfo();
        PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
        entity.setClerkId(userInfo.getId());
        entity.setDataType("4");
        entity.setReviewState("0");
        entity.setDataContent(Collections.singletonList(String.valueOf(vo.getSex())));
        playClerkDataReviewInfoService.create(entity);
        return R.ok().message("提交成功，等待审核~");
    }

    @ApiOperation(value = "更新头像", notes = "店员更新头像")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @ClerkUserLogin
    @PostMapping("/user/updateAvatar")
    public R updateAvatar(@ApiParam(value = "头像信息", required = true) @Validated @RequestBody PlayClerkUserAvatarVo vo) {
        PlayClerkUserInfoEntity userInfo = ThreadLocalRequestDetail.getClerkUserInfo();
        // PlayClerkDataReviewInfoEntity entity =
        // playClerkDataReviewInfoService.queryByClerkId(userInfo.getId(), "1", "0");
        // if (entity != null) {
        // throw new CustomException("已有申请未审核");
        // }
        PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
        entity.setClerkId(userInfo.getId());
        entity.setDataType("1");
        entity.setReviewState("0");
        List<String> list = new ArrayList<>();
        list.add(vo.getAvatar());
        entity.setDataContent(list);
        playClerkDataReviewInfoService.create(entity);
        return R.ok().message("提交成功，等待审核~");
    }

    @ClerkUserLogin
    @PostMapping("/user/updateAlbum")
    public R updateAlbum(@Validated @RequestBody PlayClerkUserAlbumVo vo) {
        PlayClerkUserInfoEntity userInfo = ThreadLocalRequestDetail.getClerkUserInfo();
        List<String> requested = vo.getAlbum() == null ? new ArrayList<>() : vo.getAlbum().stream()
                .filter(StrUtil::isNotBlank)
                .map(String::trim)
                .distinct()
                .collect(Collectors.toList());

        // 查询当前所有已审核通过的 PROFILE 媒资
        List<PlayClerkMediaAssetEntity> approvedAssets = clerkMediaAssetService.listByState(
                userInfo.getId(),
                ClerkMediaUsage.PROFILE,
                Collections.singletonList(ClerkMediaReviewState.APPROVED));

        LinkedHashSet<String> requestedSet = new LinkedHashSet<>(requested);
        if (requestedSet.isEmpty()) {
            throw new CustomException("最少上传一张照片");
        }

        // 计算哪些是新媒资（需走审核），哪些是纯删除/排序
        java.util.Set<String> approvedIds = approvedAssets.stream()
                .map(PlayClerkMediaAssetEntity::getMediaId)
                .filter(StrUtil::isNotBlank)
                .collect(java.util.stream.Collectors.toSet());
        java.util.Set<String> newMediaIds = requestedSet.stream()
                .filter(id -> !approvedIds.contains(id))
                .collect(java.util.stream.Collectors.toSet());

        if (log.isInfoEnabled()) {
            log.info("[ClerkAlbumUpdate] clerkId={} tenantId={} requestedSet={} approvedIds={} newMediaIds={}",
                    userInfo.getId(), userInfo.getTenantId(), requestedSet, approvedIds, newMediaIds);
        }

        if (!newMediaIds.isEmpty()) {
            // 新增媒资必须是当前店员本人名下、已就绪的媒资，才能进入审核流程
            java.util.List<com.starry.admin.modules.media.entity.PlayMediaEntity> newMediaEntities =
                    mediaService.lambdaQuery()
                            .in(com.starry.admin.modules.media.entity.PlayMediaEntity::getId, newMediaIds)
                            .list();

            java.util.Set<String> existingMediaIds = newMediaEntities.stream()
                    .map(com.starry.admin.modules.media.entity.PlayMediaEntity::getId)
                    .collect(java.util.stream.Collectors.toSet());
            java.util.Set<String> missingMediaIds = new java.util.HashSet<>(newMediaIds);
            missingMediaIds.removeAll(existingMediaIds);

            if (!missingMediaIds.isEmpty()) {
                // 这里很可能是历史相册里的纯 URL（未经过媒资化），我们记录日志但不直接失败，
                // 在审核内容中仍然保留这些字符串，由审核端用回显逻辑处理。
                log.warn(
                        "[ClerkAlbumUpdate] some album entries not found in play_media, treating as legacy values, clerkId={} tenantId={} missingIds={} existingIds={}",
                        userInfo.getId(),
                        userInfo.getTenantId(),
                        missingMediaIds,
                        existingMediaIds);
            }

            if (log.isInfoEnabled()) {
                log.info(
                        "[ClerkAlbumUpdate] loaded newMediaEntities for validation, clerkId={} tenantId={} mediaSummaries={}",
                        userInfo.getId(),
                        userInfo.getTenantId(),
                        newMediaEntities.stream()
                                .map(m -> String.format("id=%s,status=%s,ownerType=%s,ownerId=%s,tenantId=%s",
                                        m.getId(), m.getStatus(), m.getOwnerType(), m.getOwnerId(), m.getTenantId()))
                                .collect(java.util.stream.Collectors.toList()));
            }

            for (com.starry.admin.modules.media.entity.PlayMediaEntity media : newMediaEntities) {
                boolean tenantMatched = userInfo.getTenantId().equals(media.getTenantId());
                boolean ownerTypeMatched = MediaOwnerType.CLERK.equals(media.getOwnerType());
                boolean ownerIdMatched = userInfo.getId().equals(media.getOwnerId());
                boolean statusReady = com.starry.admin.modules.media.enums.MediaStatus.READY.getCode()
                        .equals(media.getStatus());

                if (!tenantMatched || !ownerTypeMatched || !ownerIdMatched || !statusReady) {
                    log.warn(
                            "[ClerkAlbumUpdate] invalid new media for clerk, clerkId={} tenantId={} mediaId={} mediaStatus={} mediaTenantId={} mediaOwnerType={} mediaOwnerId={} tenantMatched={} ownerTypeMatched={} ownerIdMatched={} statusReady={}",
                            userInfo.getId(),
                            userInfo.getTenantId(),
                            media.getId(),
                            media.getStatus(),
                            media.getTenantId(),
                            media.getOwnerType(),
                            media.getOwnerId(),
                            tenantMatched,
                            ownerTypeMatched,
                            ownerIdMatched,
                            statusReady);
                    throw new CustomException("存在无效的照片/视频，请刷新后重试");
                }
                if (!statusReady) {
                    log.warn(
                            "[ClerkAlbumUpdate] media not in READY state for clerk, clerkId={} tenantId={} mediaId={} mediaStatus={}",
                            userInfo.getId(),
                            userInfo.getTenantId(),
                            media.getId(),
                            media.getStatus());
                    throw new CustomException("存在未完成上传的照片/视频，请稍后重试");
                }
            }

            // 只要存在新增媒资，则按原有逻辑走资料审核，由审核通过时统一生效
            PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
            entity.setClerkId(userInfo.getId());
            entity.setDataType("2");
            entity.setReviewState("0");
            entity.setDataContent(new ArrayList<>(requestedSet));
            playClerkDataReviewInfoService.create(entity);
            return R.ok().message("提交成功，等待审核~");
        }

        // 仅删除/排序：直接应用变更，不再生成审核记录
        // 先根据新的顺序更新 orderIndex
        clerkMediaAssetService.reorder(userInfo.getId(), ClerkMediaUsage.PROFILE, new ArrayList<>(requestedSet));

        // 再对不再保留的媒资执行软删除
        java.util.Set<String> requestedOnly = new java.util.HashSet<>(requestedSet);
        java.util.Set<String> deletedMediaIds = approvedIds.stream()
                .filter(id -> !requestedOnly.contains(id))
                .collect(java.util.stream.Collectors.toSet());
        for (String mediaId : deletedMediaIds) {
            clerkMediaAssetService.softDelete(userInfo.getId(), mediaId);
            mediaService.softDelete(MediaOwnerType.CLERK, userInfo.getId(), mediaId);
        }

        return R.ok().message("修改成功");
    }

    @ClerkUserLogin
    @PostMapping("/user/updateAudio")
    public R updateAudio(@Validated @RequestBody PlayClerkUserAudioVo vo) {
        PlayClerkUserInfoEntity userInfo = ThreadLocalRequestDetail.getClerkUserInfo();
        // PlayClerkDataReviewInfoEntity entity =
        // playClerkDataReviewInfoService.queryByClerkId(userInfo.getId(), "3", "0");
        // if (entity != null) {
        // throw new CustomException("已有申请未审核");
        // }
        PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
        entity.setClerkId(userInfo.getId());
        entity.setDataType("3");
        entity.setReviewState("0");
        List<String> list = new ArrayList<>();
        list.add(vo.getAudio());
        entity.setDataContent(list);
        playClerkDataReviewInfoService.create(entity);
        return R.ok().message("提交成功，等待审核~");
    }

    @ClerkUserLogin
    @PostMapping("/user/updateOnlineState")
    public R updateAudio(@Validated @RequestBody PlayClerkUserOnlineStateVo vo) {
        String userId = ThreadLocalRequestDetail.getClerkUserInfo().getId();
        PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity();
        entity.setOnlineState(vo.getOnlineState());
        entity.setId(userId);
        playClerkUserInfoService.update(entity);
        return R.ok("修改成功");
    }

    @ClerkUserLogin
    @PostMapping("/user/updateCommodity")
    public R updateAudio(@Validated @RequestBody PlayClerkCommodityEditVo vo) {
        playClerkCommodityService.startStopClerkItem(vo.getCommodityType(), vo.getEnablingState(),
                ThreadLocalRequestDetail.getClerkUserInfo().getId());
        return R.ok("成功");
    }

    @ClerkUserLogin
    @PostMapping("/user/updateOther")
    public R updateOther(@Validated @RequestBody PlayClerkUserOtherVo vo) {
        if (vo.getSex() != null) {
            throw new CustomException("性别修改需要提交资料审核");
        }
        vo.setSex(null);
        PlayClerkUserInfoEntity userInfo = ConvertUtil.entityToVo(vo, PlayClerkUserInfoEntity.class);
        userInfo.setId(ThreadLocalRequestDetail.getClerkUserInfo().getId());
        playClerkUserInfoService.update(userInfo);
        return R.ok("申请成功");
    }

    /**
     * 查询陪聊服务项目列表
     */
    @GetMapping("/user/listAllCommodity")
    public R listAllCommodity() {
        List<PlayClerkCommodityEntity> list = playClerkCommodityService.selectAll();
        return R.ok(ConvertUtil.entityToVoList(list, PlayClerkCommodityQueryVo.class));
    }

    /**
     * 分页获取店员列表
     *
     * @param vo PlayClerkUserInfoQueryVo
     * @return 店员列表
     */
    @PostMapping("/user/queryByPage")
    public TypedR<IPage<PlayClerkUserInfoResultVo>> queryByPage(@RequestBody PlayClerkUserInfoQueryVo vo) {
        IPage<PlayClerkUserInfoResultVo> page = playClerkUserInfoService.selectByPage(vo,
                customUserService.getLoginUserId());
        return TypedR.ok(page);
    }

    /**
     * 获取推荐用户列表
     *
     * @return 店员列表
     */
    @PostMapping("/user/queryByRecommend")
    public R queryByRecommend() {
        PlayClerkUserInfoQueryVo vo = new PlayClerkUserInfoQueryVo();
        vo.setPageNum(1);
        vo.setPageSize(9999);
        vo.setRecommendationState("1");
        IPage<PlayClerkUserInfoResultVo> page = playClerkUserInfoService.selectByPage(vo, "");
        return R.ok(page);
    }

    /**
     * 获取店员礼物信息
     *
     * @param id 店员ID
     * @return 店员礼物
     */

    @GetMapping("/user/queryGiftById")
    public R queryGiftById(@RequestParam("id") String id) {
        PlayClerkUserInfoEntity entity = clerkUserInfoService.selectById(id);
        if (entity == null) {
            throw new CustomException("用户不存在");
        }
        // 获取所有礼物列表
        List<PlayGiftInfoEntity> giftInfoEntities = giftInfoService.listByAll().stream()
                .filter(gift -> StrUtil.isBlank(entity.getTenantId())
                        || entity.getTenantId().equals(gift.getTenantId()))
                .collect(Collectors.toList());
        // 获取已点亮礼物
        List<PlayClerkGiftReturnVo> clerkListByAll =
                giftInfoService.clerkListByAll(entity.getTenantId(), id, "1");
        // 组装数据
        List<PlayClerkGiftReturnVo> result = new ArrayList<>();
        for (PlayGiftInfoEntity giftInfoEntity : giftInfoEntities) {
            PlayClerkGiftReturnVo item = ConvertUtil.entityToVo(giftInfoEntity, PlayClerkGiftReturnVo.class);
            for (PlayClerkGiftReturnVo playClerkGiftReturnVo : clerkListByAll) {
                if (giftInfoEntity.getId().equals(playClerkGiftReturnVo.getId())) {
                    item.setGiffNumber(playClerkGiftReturnVo.getGiffNumber());
                }
            }
            result.add(item);
        }
        Map<String, Object> resuleMap = new HashMap<>();
        resuleMap.put("list", result);
        resuleMap.put("obtainedGift", clerkListByAll.size());
        resuleMap.put("totalGift", giftInfoEntities.size());
        return R.ok(resuleMap);
    }

    /**
     * 获取店员价格
     *
     * @param id 店员ID
     * @return 店员价格
     */
    @GetMapping("/user/queryPriceById")
    public R queryDetailById(@RequestParam("id") String id) {
        return R.ok();
    }

    /**
     * 店员分页查询本人订单列表
     *
     * @param vo 订单列表分页查询对象
     * @return com.starry.common.result.R
     * @author admin
     * @since 2024/5/8 15:57
     **/
    @ClerkUserLogin
    @PostMapping("/order/queryByPage")
    public R queryOrderByPage(@Validated @RequestBody PlayClerkOrderInfoQueryVo vo) {
        vo.setAcceptBy(ThreadLocalRequestDetail.getClerkUserInfo().getId());
        IPage<PlayClerkOrderListReturnVo> iPage = playOrderInfoService.clerkSelectOrderInfoByPage(vo);
        return R.ok(iPage);
    }

    /**
     * 店员查询本人订单详情
     *
     * @param id 订单ID
     * @return com.starry.common.result.R
     * @author admin
     * @since 2024/5/8 15:57
     **/
    @ClerkUserLogin
    @GetMapping("/order/queryById")
    public R queryById(@RequestParam("id") String id) {
        PlayClerkOrderDetailsReturnVo vo = playOrderInfoService
                .clerkSelectOrderDetails(ThreadLocalRequestDetail.getClerkUserInfo().getId(), id);
        if (StringUtils.isNotEmpty(vo.getAcceptBy()) && !vo.getAcceptBy().equals(ThreadLocalRequestDetail.getClerkUserInfo().getId())) {
            vo.setWeiChatCode("");
        }
        if (vo.getOrderStatus().equals("4")) {
            vo.setWeiChatCode("");
        }
        return R.ok(vo);
    }

    /**
     * 店员-接单
     *
     * @param id 订单ID
     * @return com.starry.common.result.R
     * @author admin
     * @since 2024/5/8 15:57
     **/
    @ClerkUserLogin
    @GetMapping("/order/accept")
    public R acceptOrder(@RequestParam("id") String id) {
        PlayClerkUserInfoEntity clerkUserInfo = ThreadLocalRequestDetail.getClerkUserInfo();
        wxCustomMpService.checkSubscribeThrowsExp(clerkUserInfo.getOpenid(), clerkUserInfo.getTenantId());
        playOrderInfoService.updateStateTo1("1", clerkUserInfo.getId(),
                clerkUserInfo.getId(), id);
        return R.ok("成功");
    }

    @GetMapping("/checkSubscribe")
    @ClerkUserLogin
    public R checkSubscribe() {
        String openid = ThreadLocalRequestDetail.getClerkUserInfo().getOpenid();
        String tenantId = SecurityUtils.getTenantId();
        return R.ok(wxCustomMpService.checkSubscribe(openid, tenantId));
    }


    /**
     * 店员-开始订单
     *
     * @param id 订单ID
     * @return com.starry.common.result.R
     * @author admin
     * @since 2024/5/8 15:57
     **/
    @ClerkUserLogin
    @GetMapping("/order/start")
    public R startOrder(@RequestParam("id") String id) {
        playOrderInfoService.updateStateTo23("1", ThreadLocalRequestDetail.getClerkUserInfo().getId(), "2", id);
        return R.ok("成功");
    }

    @ClerkUserLogin
    @PostMapping("/order/complete")
    public R completeOrder(@Validated @RequestBody PlayOrderCompleteVo vo) {
        PlayClerkUserInfoEntity clerkInfo = ThreadLocalRequestDetail.getClerkUserInfo();
        OperatorType operatorType = resolveManagementOperatorType(clerkInfo);
        String operatorId = operatorType == OperatorType.GROUP_LEADER ? clerkInfo.getId() : clerkInfo.getSysUserId();
        playOrderInfoService.completeOrderByManagement(
                operatorType.getCode(),
                operatorId,
                vo.getOrderId(),
                StrUtil.isNotBlank(vo.getRemark()) ? vo.getRemark().trim() : null);
        return R.ok("操作成功");
    }

    private OperatorType resolveManagementOperatorType(PlayClerkUserInfoEntity clerkInfo) {
        if (clerkInfo == null || StrUtil.isBlank(clerkInfo.getSysUserId())) {
            throw new CustomException("账号未绑定系统用户，无法完成订单");
        }
        if (playPersonnelAdminInfoService.selectByUserId(clerkInfo.getSysUserId()) != null) {
            return OperatorType.ADMIN;
        }
        if (playPersonnelGroupInfoService.selectByUserId(clerkInfo.getSysUserId()) != null) {
            return OperatorType.GROUP_LEADER;
        }
        throw new CustomException("当前账号无权完成订单");
    }

    /**
     * 店员-取消订单
     *
     * @param vo 取消订单传参
     * @return com.starry.common.result.R
     * @author admin
     * @since 2024/5/8 15:57
     **/
    @ClerkUserLogin
    @PostMapping("/order/cancellation")
    public R endOrder(@Validated @RequestBody PlayOrderStateEditVo vo) {
        playOrderInfoService.updateStateTo4("1", ThreadLocalRequestDetail.getClerkUserInfo().getId(), vo.getOrderId(),
                vo.getRefundReason(), vo.getImages());
        return R.ok("成功");
    }

    /**
     * 分页获取店员评价（订单评价）
     *
     * @param vo 店员评价查询对象
     * @return 店员评价
     */
    @PostMapping("/user/queryEvaluateByPage")
    public R queryEvaluateById(@Validated @RequestBody PlayOrderEvaluateQueryVo vo) {
        clerkUserInfoService.selectById(vo.getClerkId());
        // Force filter to show only visible evaluations (hidden = "0") for WeChat client
        vo.setHidden(com.starry.common.enums.EvaluateHiddenState.VISIBLE.getCode());
        return R.ok(playOrderEvaluateInfoService.selectByPage(vo));
    }

    @GetMapping("/level/queryAll")
    public R userAdd() {
        return R.ok(playClerkLevelInfoService.selectAll());
    }

    @GetMapping("/class/queryAll")
    public R queryClassAll() {
        return R.ok(playClerkClassificationInfoService.selectAll());
    }

}
