package com.fs.qw.service.impl;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.fs.common.core.domain.R;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.exception.CustomException;
import com.fs.common.service.ISmsService;
import com.fs.common.utils.DateUtils;
import com.fs.common.utils.StringUtils;
import com.fs.company.domain.CompanySms;
import com.fs.company.domain.CompanySmsTemp;
import com.fs.company.service.ICompanySmsService;
import com.fs.company.service.ICompanySmsTempService;
import com.fs.his.dto.SendResultDetailDTO;
import com.fs.qw.bo.SendMsgLogBo;
import com.fs.qw.domain.QwAcquisitionAssistant;
import com.fs.qw.domain.QwUser;
import com.fs.qw.dto.BatchAddAcquisitionLinkDTO;
import com.fs.qw.dto.IpadBlindAddDto;
import com.fs.qw.enums.SmsLogType;
import com.fs.qw.mapper.QwAcquisitionAssistantMapper;
import com.fs.qw.mapper.QwUserMapper;
import com.fs.qw.param.QwUserListParam;
import com.fs.qw.utils.UniqueStringUtil;
import com.fs.qw.vo.QwUserVO;
import com.fs.wxwork.dto.WxAddSearchDTO;
import com.fs.wxwork.dto.WxSearchContactDTO;
import com.fs.wxwork.dto.WxSearchContactResp;
import com.fs.wxwork.dto.WxWorkResponseDTO;
import com.fs.wxwork.service.WxWorkService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import com.fs.qw.mapper.QwAcquisitionLinkInfoMapper;
import com.fs.qw.domain.QwAcquisitionLinkInfo;
import com.fs.qw.service.IQwAcquisitionLinkInfoService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;

import static com.fs.his.utils.PhoneUtil.encryptPhone;

/**
 * 获客链接-号码链接生成记录Service业务层处理
 *
 * @author fs
 * @date 2026-03-27
 */
@Slf4j
@Service
public class QwAcquisitionLinkInfoServiceImpl implements IQwAcquisitionLinkInfoService
{
    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private ISmsService smsService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ICompanySmsTempService smsTempService;

    @Autowired
    private ICompanySmsService companySmsService;

    @Autowired
    private QwAcquisitionAssistantMapper acquisitionAssistantMapper;

    @Autowired
    private QwAcquisitionLinkInfoMapper qwAcquisitionLinkInfoMapper;

    @Autowired
    private QwUserMapper qwUserMapper;

    @Autowired
    private WxWorkService wxWorkService;

    @PostConstruct
    public void init() {
        // 如果自动注入失败，手动获取
        if (wxWorkService == null) {
            wxWorkService = applicationContext.getBean(WxWorkService.class);
        }
    }
    //拼接电话号码的链接后缀(这个后面拼接加密后的手机字符串)
    private static final String  LINK_SUFFIX = "?customer_channel=up:";
    private static final String  LINK_SUFFIX_NOL = "?customer_channel=link:";

    // 企微加好友链接-url的key
    private static final String QW_FRIEND_LINK_URL_KEY = "qw_friend_link_url:";

    //获客链接短信模板code
    private static final String  SMS_LINK_TEMPLATE_CODE = "获客链接短信模板";

    //访问链接域名
    private static final String  LINK_DOMAIN = "https://c.ysyd.top/";
    /**
     * 查询获客链接-号码链接生成记录
     *
     * @param id 获客链接-号码链接生成记录主键
     * @return 获客链接-号码链接生成记录
     */
    @Override
    public QwAcquisitionLinkInfo selectQwAcquisitionLinkInfoById(Long id)
    {
        return qwAcquisitionLinkInfoMapper.selectQwAcquisitionLinkInfoById(id);
    }

    /**
     * 查询获客链接-号码链接生成记录列表
     *
     * @param qwAcquisitionLinkInfo 获客链接-号码链接生成记录
     * @return 获客链接-号码链接生成记录
     */
    @Override
    public List<QwAcquisitionLinkInfo> selectQwAcquisitionLinkInfoList(QwAcquisitionLinkInfo qwAcquisitionLinkInfo)
    {
        List<QwAcquisitionLinkInfo> resultList = qwAcquisitionLinkInfoMapper.selectQwAcquisitionLinkInfoList(qwAcquisitionLinkInfo);
        if (CollectionUtils.isEmpty(resultList)){
            return Collections.emptyList();
        }
        for (QwAcquisitionLinkInfo item : resultList) {
            if (item.getPhone() != null) {
                item.setPhone(item.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
            }
        }
        return resultList;
    }

    /**
     * 新增获客链接-号码链接生成记录
     *
     * @param qwAcquisitionLinkInfo 获客链接-号码链接生成记录
     * @return 结果
     */
    @Override
    public int insertQwAcquisitionLinkInfo(QwAcquisitionLinkInfo qwAcquisitionLinkInfo)
    {
        return qwAcquisitionLinkInfoMapper.insertQwAcquisitionLinkInfo(qwAcquisitionLinkInfo);
    }

    /**
     * 修改获客链接-号码链接生成记录
     *
     * @param qwAcquisitionLinkInfo 获客链接-号码链接生成记录
     * @return 结果
     */
    @Override
    public int updateQwAcquisitionLinkInfo(QwAcquisitionLinkInfo qwAcquisitionLinkInfo)
    {
        //TODO 如果放开编辑就需要更新缓存
        return qwAcquisitionLinkInfoMapper.updateQwAcquisitionLinkInfo(qwAcquisitionLinkInfo);
    }

    /**
     * 批量删除获客链接-号码链接生成记录
     *
     * @param ids 需要删除的获客链接-号码链接生成记录主键集合
     * @return 结果
     */
    @Override
    public int deleteQwAcquisitionLinkInfoByIds(Long[] ids)
    {
        if (ids.length == 0){
            return 0;
        }
        List<QwAcquisitionLinkInfo> toBeDeletedList = qwAcquisitionLinkInfoMapper.selectAcquisitionLinkInfoListByIds(ids);
        //循环删除 Redis 缓存
        batchDeleteLinkCatch(toBeDeletedList);
        return qwAcquisitionLinkInfoMapper.deleteQwAcquisitionLinkInfoByIds(ids);
    }

    /**
     * 删除获客链接-号码链接生成记录信息
     *
     * @param qwAcquisitionAssistantIds 需要删除的获客链接管理主键ID集合
     * @return 结果
     */
    @Override
    public int deleteQwAcquisitionLinkInfoByQwAcquisitionAssistantIds(Long[] qwAcquisitionAssistantIds){
        if (qwAcquisitionAssistantIds.length == 0){
            return 0;
        }
        List<QwAcquisitionLinkInfo> toBeDeletedList = qwAcquisitionLinkInfoMapper.selectAcquisitionLinkInfoListByAcquisitionAssistantIds(qwAcquisitionAssistantIds);
        //循环删除 Redis 缓存
        batchDeleteLinkCatch(toBeDeletedList);
        return qwAcquisitionLinkInfoMapper.deleteQwAcquisitionLinkInfoByQwAcquisitionAssistantIds(qwAcquisitionAssistantIds);
    }

    /**
     * 删除获客链接-号码链接生成记录信息
     *
     * @param id 获客链接-号码链接生成记录主键
     * @return 结果
     */
    @Override
    public int deleteQwAcquisitionLinkInfoById(Long id)
    {
        QwAcquisitionLinkInfo existAcquisitionLinkInfo = qwAcquisitionLinkInfoMapper.selectQwAcquisitionLinkInfoById(id);
        if (existAcquisitionLinkInfo==null){
            throw new CustomException("数据不存在");
        }
        // ========== 删除Redis缓存 ==========
        try {
            // 1. 删除pageParam对应的URL缓存
            if (StringUtils.isNotEmpty(existAcquisitionLinkInfo.getRandomStr())) {
                String urlCacheKey = QW_FRIEND_LINK_URL_KEY + existAcquisitionLinkInfo.getRandomStr();
                redisCache.deleteObject(urlCacheKey);
                log.info("删除获客链接URL缓存成功, pageParam: {}, key: {}",
                        existAcquisitionLinkInfo.getRandomStr(), urlCacheKey);
            }
        } catch (Exception e) {
            // 缓存删除失败不应该影响主流程，但需要记录日志
            log.error("删除获客链接缓存失败, id: {}, qwAcquisitionAssistantId: {}, pageParam: {}",
                    existAcquisitionLinkInfo.getId(), existAcquisitionLinkInfo.getQwAcquisitionAssistantId(),
                    existAcquisitionLinkInfo.getRandomStr(), e);
        }
        return qwAcquisitionLinkInfoMapper.deleteQwAcquisitionLinkInfoById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SendResultDetailDTO sendMessageLink(String phone, Long qwAcquisitionId, SendMsgLogBo sendMsgLogBo) {
        log.info("发送获客链接短信(带随机参数),号码：{}", phone);

        // 1. 获取短信模板
        CompanySmsTemp temp = smsTempService.selectCompanySmsTempByCode(SMS_LINK_TEMPLATE_CODE);
        if (temp == null) {
            log.info("获客链接-未找到短信模板：{}", SMS_LINK_TEMPLATE_CODE);
            throw new CustomException("获客链接-未找到短信模板");
        }

        // 2. 获取获客链接信息
        String originalContent = temp.getContent();
        QwAcquisitionAssistant acquisitionAssistant = acquisitionAssistantMapper.selectQwAcquisitionAssistantById(qwAcquisitionId);
        if (acquisitionAssistant == null) {
            log.info("获客链接-未找到获客链接id:{}", qwAcquisitionId);
            throw new CustomException("获客链接-未找到获客链接信息");
        }

        // 3. 生成随机参数并构建短信内容
        String randomStr = generateUniqueRandomStr();
        String replaceText = LINK_DOMAIN + randomStr;
        String content = originalContent.replace("${sms.friendLink}", replaceText);

        // 4. 计算需要的短信条数
        Integer needCount = calculateSmsCount(content);

        // 5. 【乐观锁扣减，支持重试】
        int maxRetries = 3;

        for (int retryCount = 0; retryCount < maxRetries; retryCount++) {
            Long balance = companySmsService.getBalance(2L);
            if (balance == null || balance < needCount) {
                log.warn("短信余额不足, companyId=2, balance={}, needCount={}", balance, needCount);
                return new SendResultDetailDTO(false, "短信余额不足", null);
            }

            CompanySms latestSms = companySmsService.selectCompanySms(2L);
            if (latestSms == null) {
                throw new CustomException("公司短信配置不存在");
            }

            if (latestSms.getRemainSmsCount() < needCount) {
                log.warn("短信余额不足, companyId=2, remainCount={}, needCount={}",
                        latestSms.getRemainSmsCount(), needCount);
                return new SendResultDetailDTO(false, "短信余额不足", null);
            }

            Long version = latestSms.getVersion() != null ? latestSms.getVersion() : 0L;
            int updateCount = companySmsService.decrementRemainSmsCountWithVersion(2L, needCount, version);

            if (updateCount > 0) {
                log.info("乐观锁扣减成功, needCount={}, version={}", needCount, version);
                break;
            }

            log.warn("乐观锁扣减失败，第{}次重试", retryCount + 1);

            if (retryCount == maxRetries - 1) {
                return new SendResultDetailDTO(false, "系统繁忙，请稍后重试", null);
            }

            try {
                Thread.sleep(50L * (retryCount + 1));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return new SendResultDetailDTO(false, "操作被中断", null);
            }
        }

        // 6. 更新缓存
        companySmsService.updateCacheBalance(2L, -needCount);

        try {
            sendMsgLogBo.setQwAcquisitionId(acquisitionAssistant.getId());

            R r = smsService.simpleSmsSend(phone, content, temp, SmsLogType.ACQUISITION_LINK, sendMsgLogBo);

            if (r != null && "200".equals(String.valueOf(r.get("code")))) {
                addAcquisitionLinkInfo(acquisitionAssistant.getId(), phone, acquisitionAssistant.getUrl(),
                        randomStr, sendMsgLogBo.getCompanyUserId());
                log.info("短信发送成功, phone={}, needCount={}", phone, needCount);
                return new SendResultDetailDTO(true, null, null);
            } else {
                String msg = r != null && r.get("msg") != null ? r.get("msg").toString() : "未知错误";
                log.warn("短信发送失败，退还余额, phone={}, needCount={}", phone, needCount);
                rollbackBalance(needCount, maxRetries);
                return new SendResultDetailDTO(false, msg, null);
            }
        } catch (Exception e) {
            log.error("发送异常，退还余额, phone=" + phone, e);
            rollbackBalance(needCount, maxRetries);
            return new SendResultDetailDTO(false, e.getMessage(), null);
        }
    }

    @Override
    public String selectQwAcquisitionUrlByRandomStr(String randomStr) {
        String key = QW_FRIEND_LINK_URL_KEY + randomStr;
        String fullLink = null;

        try {
            Object cacheObj = redisCache.getCacheObject(key);
            if (cacheObj instanceof String) {
                fullLink = (String) cacheObj;
                // 处理缓存空值的情况
                if ("NULL".equals(fullLink)) {
                    return null;
                }
                log.debug("从缓存获取完整获客链接url成功,randomStr:{}", randomStr);
                return fullLink;
            }
        } catch (Exception e) {
            log.warn("从缓存获取完整获客链接url异常, 将重新获取, randomStr:{}", randomStr);
        }

        // 缓存中没有，查询数据库
        fullLink = qwAcquisitionLinkInfoMapper.selectQwAcquisitionUrlByRandomStr(randomStr);

        // 缓存处理（包括空值缓存）
        if (fullLink == null) {
            int nullCacheExpire = 10; // 10秒
            redisCache.setCacheObject(key, "NULL", nullCacheExpire, TimeUnit.SECONDS);
            log.info("完整获客链接URL不存在，缓存空值10秒, randomStr:{}", randomStr);
            return null;
        } else {
            // 正常值仍缓存2天
            Integer cacheExpire = 2;
            redisCache.setCacheObject(key, fullLink, cacheExpire, TimeUnit.DAYS);
            log.info("完整获客链接URL缓存成功, randomStr:{}", randomStr);
        }

        return fullLink;
    }

    @Override
    //TODO 多数据批量操作时，当前事务还未提交，在短时间内可能会导致第三方回调的时候查不到需要更新的数据，后续优化
    //@Transactional(rollbackFor = Exception.class)
    public int batchCreateMessageLink(BatchAddAcquisitionLinkDTO batchAddAcquisitionLinkDTO) {
        Long qwAcquisitionId = batchAddAcquisitionLinkDTO.getQwAcquisitionAssistantId();
        List<String> phoneList = batchAddAcquisitionLinkDTO.getPhoneList();

        if (CollectionUtils.isEmpty(phoneList)) {
            log.warn("批量发送短信，手机号列表为空");
            return 0;
        }

        // 1. 获取短信模板
        CompanySmsTemp temp = smsTempService.selectCompanySmsTempByCode(SMS_LINK_TEMPLATE_CODE);
        if (temp == null) {
            log.info("获客链接-未找到短信模板：{}", SMS_LINK_TEMPLATE_CODE);
            throw new CustomException("获客链接-未找到短信模板");
        }

        // 2. 获取获客链接信息
        QwAcquisitionAssistant acquisitionAssistant = acquisitionAssistantMapper.selectQwAcquisitionAssistantById(qwAcquisitionId);
        if (acquisitionAssistant == null) {
            log.info("获客链接-未找到获客链接id:{}", qwAcquisitionId);
            throw new CustomException("获客链接-未找到获客链接信息");
        }

        String originalContent = temp.getContent();
        SendMsgLogBo baseSendMsgLogBo = new SendMsgLogBo();
        baseSendMsgLogBo.setCompanyUserId(batchAddAcquisitionLinkDTO.getCreateBy());
        baseSendMsgLogBo.setQwAcquisitionId(acquisitionAssistant.getId());

        // 3. 为每个手机号预生成链接和短信内容
        List<PhoneMessageInfo> phoneMessageList = new ArrayList<>();
        for (String phone : phoneList) {
            String randomStr = generateUniqueRandomStr();
            String replaceText = LINK_DOMAIN + randomStr;
            String content = originalContent.replace("${sms.friendLink}", replaceText);
            Integer needCount = calculateSmsCount(content);

            PhoneMessageInfo info = new PhoneMessageInfo();
            info.setPhone(phone);
            info.setContent(content);
            info.setRandomStr(randomStr);
            info.setNeedCount(needCount);
            phoneMessageList.add(info);
        }

        // 4. 计算总需要条数
        int totalNeedCount = phoneMessageList.stream().mapToInt(PhoneMessageInfo::getNeedCount).sum();

        // 5. 快速校验总余额是否充足（使用缓存）
        Long balance = companySmsService.getBalance(2L);
        if (balance == null || balance < totalNeedCount) {
            // 缓存可能不准，再查一次DB确认
            CompanySms companySms = companySmsService.selectCompanySms(2L);
            if (companySms == null) {
                throw new CustomException("公司短信配置不存在");
            }
            if (companySms.getRemainSmsCount() < totalNeedCount) {
                throw new CustomException("短信余额不足，总需要：" + totalNeedCount +
                        "，当前余额：" + companySms.getRemainSmsCount());
            }
        }

        // 6. 批量发送短信
        List<SendResultInfo> sendResults = new ArrayList<>();
        int successCount = 0;

        for (PhoneMessageInfo info : phoneMessageList) {
            try {
                SendMsgLogBo sendMsgLogBo = new SendMsgLogBo();
                BeanUtils.copyProperties(baseSendMsgLogBo, sendMsgLogBo);

                R r = smsService.simpleSmsSend(info.getPhone(), info.getContent(), temp,
                        SmsLogType.ACQUISITION_LINK, sendMsgLogBo);

                if (r != null && "200".equals(String.valueOf(r.get("code")))) {
                    addAcquisitionLinkInfo(acquisitionAssistant.getId(), info.getPhone(),
                            acquisitionAssistant.getUrl(), info.getRandomStr(),
                            baseSendMsgLogBo.getCompanyUserId());

                    SendResultInfo successResult = new SendResultInfo();
                    successResult.setSuccess(true);
                    successResult.setNeedCount(info.getNeedCount());
                    successResult.setPhone(info.getPhone());
                    sendResults.add(successResult);
                    successCount++;

                    log.info("短信发送成功 phone={}, needCount={}", info.getPhone(), info.getNeedCount());
                } else {
                    String msg = r != null && r.get("msg") != null ? r.get("msg").toString() : "未知错误";
                    log.warn("短信发送失败 phone={}, msg={}", info.getPhone(), msg);

                    SendResultInfo failResult = new SendResultInfo();
                    failResult.setSuccess(false);
                    failResult.setNeedCount(0);
                    failResult.setPhone(info.getPhone());
                    failResult.setFailReason(msg);
                    sendResults.add(failResult);
                }
            } catch (Exception e) {
                log.error("短信发送异常 phone=" + info.getPhone(), e);

                SendResultInfo failResult = new SendResultInfo();
                failResult.setSuccess(false);
                failResult.setNeedCount(0);
                failResult.setPhone(info.getPhone());
                failResult.setFailReason(e.getMessage());
                sendResults.add(failResult);
            }
        }

        // 7. 计算实际需要扣减的总条数
        int actualNeedCount = sendResults.stream()
                .filter(SendResultInfo::isSuccess)
                .mapToInt(SendResultInfo::getNeedCount)
                .sum();

        if (actualNeedCount == 0) {
            log.info("批量发送全部失败，不扣减余额");
            return 0;
        }

        // 8. 乐观锁扣减，支持重试
        int maxRetries = 3;

        for (int retryCount = 0; retryCount < maxRetries; retryCount++) {
            // 快速检查（用缓存）
            Long reBalance = companySmsService.getBalance(2L);
            if (reBalance == null || reBalance < actualNeedCount) {
                // 缓存可能不准，再查一次DB确认
                CompanySms latestSms = companySmsService.selectCompanySms(2L);
                if (latestSms == null) {
                    throw new CustomException("公司短信配置不存在");
                }
                if (latestSms.getRemainSmsCount() < actualNeedCount) {
                    throw new CustomException("短信余额不足");
                }
            }

            // 获取最新数据（含version）
            CompanySms latestSms = companySmsService.selectCompanySms(2L);
            if (latestSms == null) {
                throw new CustomException("公司短信配置不存在");
            }

            Long version = latestSms.getVersion() != null ? latestSms.getVersion() : 0L;
            int updateCount = companySmsService.decrementRemainSmsCountWithVersion(2L, actualNeedCount, version);

            if (updateCount > 0) {
                log.info("乐观锁扣减余额成功, companyId=2, needCount={}, version={}", actualNeedCount, version);
                // 扣减成功，更新缓存
                companySmsService.updateCacheBalance(2L, -actualNeedCount);
                log.info("批量创建获客链接完成，总计尝试 {}, 成功 {}, 实际扣减条数 {}",
                        phoneList.size(), successCount, actualNeedCount);
                return successCount;
            }

            log.warn("乐观锁扣减余额失败，第{}次重试, version={}", retryCount + 1, version);

            if (retryCount == maxRetries - 1) {
                throw new CustomException("扣减余额失败，系统繁忙，请稍后重试");
            }

            try {
                Thread.sleep(100L * (retryCount + 1));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new CustomException("扣减余额被中断");
            }
        }

        return successCount;
    }

    /**
     * 退还余额（带重试）
     */
    private void rollbackBalance(int needCount, int maxRetries) {
        for (int i = 0; i < maxRetries; i++) {
            try {
                CompanySms current = companySmsService.selectCompanySms(2L);
                if (current == null) {
                    log.error("退还短信余额失败，公司短信配置不存在");
                    return;
                }
                Long currentVersion = current.getVersion() != null ? current.getVersion() : 0L;
                int rollbackCount = companySmsService.incrementRemainSmsCountWithVersion(2L, needCount, currentVersion);
                if (rollbackCount > 0) {
                    companySmsService.updateCacheBalance(2L, needCount);
                    log.info("退还余额成功, needCount={}", needCount);
                    return;
                }
                Thread.sleep(50L);
            } catch (Exception e) {
                log.error("退还短信余额异常，第{}次重试", i + 1, e);
            }
        }
        log.error("退还短信余额失败，需要人工处理, companyId=2, needCount={}", needCount);
    }

    @Override
    public String extractLink(Long qwAcquisitionAssistantId, String originalPhone, String originalLink,Long createBy) {
        String randomStr = generateUniqueRandomStr();
        QwAcquisitionLinkInfo qwAcquisitionLinkInfo=new QwAcquisitionLinkInfo();
        qwAcquisitionLinkInfo.setQwAcquisitionAssistantId(qwAcquisitionAssistantId);
        qwAcquisitionLinkInfo.setCreateBy(createBy);
        qwAcquisitionLinkInfo.setCreateTime(DateUtils.getTime());
        qwAcquisitionLinkInfo.setPhone(originalPhone);//这里存储原始手机号
        //加密手机号
        String phonePlus = encryptPhone(originalPhone);
        String linkPlus=originalLink+LINK_SUFFIX+ phonePlus;
        qwAcquisitionLinkInfo.setLink(linkPlus);
        qwAcquisitionLinkInfo.setRandomStr(randomStr);
        int addResult=qwAcquisitionLinkInfoMapper.insertQwAcquisitionLinkInfo(qwAcquisitionLinkInfo);
        // ========== 缓存URL，便于后续通过randomStr访问 ==========
        try {
            String cacheKey = QW_FRIEND_LINK_URL_KEY + randomStr;
            Integer cacheExpire = 2; // 默认缓存2天
            redisCache.setCacheObject(cacheKey, linkPlus, cacheExpire, TimeUnit.DAYS);
            log.info("获客链接URL缓存成功, pageParam: {}, url: {}", randomStr, linkPlus);
        } catch (Exception e) {
            // 缓存失败不影响主流程，但需要记录日志
            log.error("获客链接URL缓存失败, pageParam: {}", randomStr, e);
        }
        // 返回域名+随机字符串
        return LINK_DOMAIN+randomStr;
    }

    @Override
    public R extractLinkNol(Long qwAcquisitionAssistantId, String originalLink, Long createBy) {
        String randomStr = generateUniqueRandomStr();
        QwAcquisitionLinkInfo qwAcquisitionLinkInfo=new QwAcquisitionLinkInfo();
        qwAcquisitionLinkInfo.setQwAcquisitionAssistantId(qwAcquisitionAssistantId);
        qwAcquisitionLinkInfo.setCreateBy(createBy);
        qwAcquisitionLinkInfo.setCreateTime(DateUtils.getTime());
        String linkPlus=originalLink+LINK_SUFFIX_NOL+ qwAcquisitionAssistantId;
        qwAcquisitionLinkInfo.setLink(linkPlus);
        qwAcquisitionLinkInfo.setRandomStr(randomStr);
        int addResult=qwAcquisitionLinkInfoMapper.insertQwAcquisitionLinkInfo(qwAcquisitionLinkInfo);
        // ========== 缓存URL，便于后续通过randomStr访问 ==========
        try {
            String cacheKey = QW_FRIEND_LINK_URL_KEY + randomStr;
            Integer cacheExpire = 2; // 默认缓存2天
            redisCache.setCacheObject(cacheKey, linkPlus, cacheExpire, TimeUnit.DAYS);
            log.info("获客链接URL缓存成功, pageParam: {}, url: {}", randomStr, linkPlus);
        } catch (Exception e) {
            // 缓存失败不影响主流程，但需要记录日志
            log.error("获客链接URL缓存失败, pageParam: {}", randomStr, e);
        }

        String shortLink = LINK_DOMAIN+randomStr;
        return R.ok().put("shortLink",shortLink).put("linkPlus",linkPlus);
        // 返回域名+随机字符串

    }

    @Override
    public void ipadBlindAdd(IpadBlindAddDto dto,SendMsgLogBo sendMsgLogBo) {
        //获取QwUser信息
        QwUser qwUser=qwUserMapper.selectQwUserById(dto.getUserId());
        if (qwUser==null){
            log.error( "销售用户不存在,qwUserId:{}",dto.getUserId());
            throw new CustomException("销售用户不存在");
        }
        //判断这个销售的ipad是否在线
        QwUserListParam queryCondition=new QwUserListParam();
        queryCondition.setCorpId(dto.getCorpId());
        queryCondition.setQwUserId(qwUser.getQwUserId());
        List<QwUserVO> qwUserVOS = qwUserMapper.selectQwUserListStaffVO(queryCondition);
        if (qwUserVOS.isEmpty()){
            throw new CustomException("此主体不存在该销售用户");
        }
        if (qwUserVOS.get(0).getIpadStatus()==0){
            throw new CustomException("此销售用户ipad已离线,请登录");
        }
        // 每次使用时获取代理对象
        QwAcquisitionLinkInfoServiceImpl self = applicationContext.getBean(QwAcquisitionLinkInfoServiceImpl.class);
        try {
            //调用ipad加好友接口
            WxWorkResponseDTO<String> response = self.qwAddWxInvokeIpad(dto.getPhone(), qwUser.getUid(),
                    qwUser.getServerId(),
                    qwUser.getVid(),
                    qwUser.getQwUserName());
            if (response != null && response.getErrcode() == 0) {
                log.info("ipad获客链接加微成功");
            }else {
                log.error("ipad获客链接加微失败,错误码:{},错误信息:{}",response.getErrcode(),response.getErrmsg());
            }
        }catch (Exception e){
            //TODO 如果有异常，暂时只做记录
            log.error("ipad加微异常",e);
        }

        //调用发送短信接口
        self.sendMessageLink(dto.getPhone(),dto.getQwAcquisitionAssistantId(),sendMsgLogBo);
    }

    /**
     * 企微加个微调用ipad端
     * @param mobile  手机号
     * @param qwUid   企微uid
     * @param serverId   服务器id
     * @return String 结果
     */
    public WxWorkResponseDTO<String> qwAddWxInvokeIpad(String mobile, String qwUid, Long serverId, String vid, String qwUserName) {
        if (StringUtils.isBlank(mobile) || StringUtils.isBlank(qwUid) || serverId == null) {
            log.warn("企微申请加好友任务参数校验失败: mobile={}, qwUid={}, serverId={}", mobile, qwUid, serverId);
            return null;
        }

        try {
            WxAddSearchDTO wxAddSearchDTO = new WxAddSearchDTO();
            wxAddSearchDTO.setUuid(qwUid);
            wxAddSearchDTO.setVid(Long.valueOf(vid));
            wxAddSearchDTO.setPhone(mobile);

            WxSearchContactDTO contactDTO=new WxSearchContactDTO();
            contactDTO.setUuid(qwUid);
            contactDTO.setPhoneNumber(mobile);


            WxWorkResponseDTO<WxSearchContactResp> respWxWorkResponseDTO = wxWorkService.searchContact(contactDTO, serverId);
            WxSearchContactResp.UserList user = respWxWorkResponseDTO.getData().getUserList().stream()
                    .filter(u -> u.getState().equals("2"))
                    .findFirst()
                    .orElse(null); // 或者 .orElseThrow(() -> new RuntimeException("未找到指定用户"))

            wxAddSearchDTO.setOptionid(user.getOpenid());
            wxAddSearchDTO.setTicket(user.getTicket());
            wxAddSearchDTO.setContent("你好，我是你的专属助手："+qwUserName+"，有什么问题都可以问我哦~");

            WxWorkResponseDTO<String> response = wxWorkService.addSearch(wxAddSearchDTO, serverId);
            log.debug("企微加微接口调用结果: errcode={}, errmsg={}",
                    response != null ? response.getErrcode() : "null",
                    response != null ? response.getErrmsg() : "null");

            return response;
        } catch (Exception e) {
            log.error("企微申请加好友任务请求接口异常: mobile={}, qwUid={}, serverId={}", mobile, qwUid, serverId, e);
            return null;
        }
    }

    /**
     * 添加链接生成记录
     * */
    public int addAcquisitionLinkInfo(Long qwAcquisitionAssistantId,String originalPhone,String originalLink,String randomStr,Long createBy){
        QwAcquisitionLinkInfo qwAcquisitionLinkInfo=new QwAcquisitionLinkInfo();
        qwAcquisitionLinkInfo.setQwAcquisitionAssistantId(qwAcquisitionAssistantId);
        qwAcquisitionLinkInfo.setCreateBy(createBy);
        qwAcquisitionLinkInfo.setCreateTime(DateUtils.getTime());
        qwAcquisitionLinkInfo.setPhone(originalPhone);//这里存储原始手机号
        //加密手机号
        String phonePlus = encryptPhone(originalPhone);
        String linkPlus=originalLink+LINK_SUFFIX+ phonePlus;
        qwAcquisitionLinkInfo.setLink(linkPlus);
        qwAcquisitionLinkInfo.setRandomStr(randomStr);
        int addResult=qwAcquisitionLinkInfoMapper.insertQwAcquisitionLinkInfo(qwAcquisitionLinkInfo);
        // ========== 缓存URL，便于后续通过randomStr访问 ==========
        try {
            String cacheKey = QW_FRIEND_LINK_URL_KEY + randomStr;
            Integer cacheExpire = 2; // 默认缓存2天
            redisCache.setCacheObject(cacheKey, linkPlus, cacheExpire, TimeUnit.DAYS);
            log.info("获客链接URL缓存成功, pageParam: {}, url: {}", randomStr, linkPlus);
        } catch (Exception e) {
            // 缓存失败不影响主流程，但需要记录日志
            log.error("获客链接URL缓存失败, pageParam: {}", randomStr, e);
        }
        return addResult;
    }


    /**
     * 生成唯一的页面参数
     */
    private String generateUniqueRandomStr() {
        // 获取所有已存在的pageParam（只取需要的字段）
        List<String> existingParams = qwAcquisitionLinkInfoMapper.selectAllRandomStr();
        //使用Set，提高查找效率 O(1)
        Set<String> paramSet = new HashSet<>(existingParams);

        int maxAttempts = 10; // 设置最大尝试次数
        int attempt = 0;

        while (attempt < maxAttempts) {
            // 生成7位随机码
            String candidate = UniqueStringUtil.generateTimeBasedUnique(7);

            // 使用Set的contains方法，O(1)复杂度
            if (!paramSet.contains(candidate)) {
                log.debug("生成页面参数成功: {}, 尝试次数: {}", candidate, attempt + 1);
                return candidate;
            }

            attempt++;
            log.debug("页面参数 {} 已存在，重新生成，第{}次尝试", candidate, attempt);
        }

        // 如果多次尝试都失败，使用+1随机数方案
        String finalParam = UniqueStringUtil.generateTimeBasedUnique(8);
        log.warn("多次尝试后使用7位参数: {}", finalParam);
        return finalParam;
    }

    /**
     *  批量删除缓存
     * */
    private void batchDeleteLinkCatch(List<QwAcquisitionLinkInfo> toBeDeletedList) {
        if (!toBeDeletedList.isEmpty()) {
            for (QwAcquisitionLinkInfo record : toBeDeletedList) {
                try {
                    // 检查 randomStr 是否为空，避免处理无效的缓存 key
                    if (StringUtils.isNotEmpty(record.getRandomStr())) {
                        String urlCacheKey = QW_FRIEND_LINK_URL_KEY + record.getRandomStr();
                        redisCache.deleteObject(urlCacheKey);
                        log.info("批量删除获客链接URL缓存成功, randomStr: {}, key: {}",
                                record.getRandomStr(), urlCacheKey);
                    }
                } catch (Exception e) {
                    // 缓存删除失败不应影响主数据库删除流程，但需要记录日志
                    log.error("批量删除获客链接缓存失败, id: {}, qwAcquisitionAssistantId: {}, randomStr: {}",
                            record.getId(), record.getQwAcquisitionAssistantId(), record.getRandomStr(), e);
                }
            }
        }
    }

    //根据短信文字内容计算短信条数
    private int calculateSmsCount(String content) {
        if (content == null) return 1;
        int counts = content.length() / 67;
        if (content.length() % 67 > 0) {
            counts = counts + 1;
        }
        if (counts == 0) {
            counts = 1;
        }
        return counts;
    }

    // 辅助类
    @Data
    private static class PhoneMessageInfo {
        private String phone;
        private String content;
        private String randomStr;
        private Integer needCount;
    }

    @Data
    private static class SendResultInfo {
        private boolean success;
        private int needCount;
        private String phone;
        private String failReason;
    }
}
