package com.ycwl.basic.pricing.service.impl;

import com.ycwl.basic.constant.BaseContextHandler;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import com.ycwl.basic.pricing.dto.req.VoucherPrintReq;
import com.ycwl.basic.pricing.dto.resp.VoucherPrintResp;
import com.ycwl.basic.pricing.entity.PriceVoucherCode;
import com.ycwl.basic.pricing.entity.PriceVoucherBatchConfig;
import com.ycwl.basic.pricing.entity.VoucherPrintRecord;
import com.ycwl.basic.pricing.mapper.PriceVoucherCodeMapper;
import com.ycwl.basic.pricing.mapper.PriceVoucherBatchConfigMapper;
import com.ycwl.basic.pricing.mapper.VoucherPrintRecordMapper;
import com.ycwl.basic.pricing.service.VoucherPrintService;
import com.ycwl.basic.printer.ticket.FeiETicketPrinter;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.utils.WxMpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 优惠券打印服务实现
 */
@Slf4j
@Service
public class VoucherPrintServiceImpl implements VoucherPrintService {
    
    @Autowired
    private VoucherPrintRecordMapper voucherPrintRecordMapper;
    
    @Autowired
    private PriceVoucherCodeMapper priceVoucherCodeMapper;
    
    @Autowired
    private PriceVoucherBatchConfigMapper priceVoucherBatchConfigMapper;
    
    @Autowired
    private FaceRepository faceRepository;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private static final String PRINT_LOCK_KEY = "voucher_print_lock:%s:%s:%s"; // faceId:brokerId:scenicId

    // 原子计数器，确保流水号唯一性
    private static final AtomicLong counter = new AtomicLong(0);
    @Autowired
    private ScenicRepository scenicRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VoucherPrintResp printVoucherTicket(VoucherPrintReq request) {
        // 参数验证
        if (request.getFaceId() == null) {
            throw new BaseException("用户faceId不能为空");
        }
        if (request.getBrokerId() == null) {
            throw new BaseException("经纪人ID不能为空");
        }
        FaceEntity face = faceRepository.getFace(request.getFaceId());
        if (face == null) {
            throw new BaseException("请上传人脸");
        }
        request.setScenicId(face.getScenicId());

        Long currentUserId = Long.valueOf(BaseContextHandler.getUserId());
        
        // 验证faceId是否属于当前用户
        validateFaceOwnership(request.getFaceId(), currentUserId);

        ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(face.getScenicId());
        Long brokerId = configManager.getLong("voucher_broker_id");
        if (brokerId != null) {
            if (!request.getBrokerId().equals(brokerId)) {
                return null;
            }
        }
        
        // 使用Redis分布式锁防止重复打印
        String lockKey = String.format(PRINT_LOCK_KEY, request.getFaceId(), request.getBrokerId(), request.getScenicId());
        String lockValue = UUID.randomUUID().toString();
        
        try {
            // 尝试获取锁，超时时间30秒
            Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);
            if (Boolean.FALSE.equals(lockAcquired)) {
                throw new BaseException("请求处理中，请稍后再试");
            }
            
            // 检查是否已存在相同的打印记录
            VoucherPrintRecord existingRecord = voucherPrintRecordMapper.selectByFaceBrokerScenic(
                    request.getFaceId(), request.getScenicId());
            
            if (existingRecord != null) {
                log.info("找到已存在的打印记录，返回该记录: {}", existingRecord.getId());
                return buildResponse(existingRecord);
            }
            
            // 获取一个可用的券码（未被打印过的）
            PriceVoucherCode availableVoucher = getAvailableUnprintedVoucher(request.getScenicId());
            if (availableVoucher == null) {
                throw new BaseException("暂无可用优惠券");
            }

            // 生成流水号
            String code = generateCode();
            
            // 创建打印记录
            VoucherPrintRecord printRecord = new VoucherPrintRecord();
            printRecord.setCode(code);
            printRecord.setFaceId(request.getFaceId());
            printRecord.setBrokerId(request.getBrokerId());
            printRecord.setScenicId(request.getScenicId());
            printRecord.setVoucherCodeId(availableVoucher.getId());
            printRecord.setVoucherCode(availableVoucher.getCode());
            printRecord.setPrintStatus(0); // 待打印
            printRecord.setCreateTime(new Date());
            printRecord.setUpdateTime(new Date());
            printRecord.setDeleted(0);
            
            voucherPrintRecordMapper.insert(printRecord);
            VoucherPrintResp voucherPrintResp = buildResponse(printRecord);

            try {
                printTicket(printRecord, voucherPrintResp);
                printRecord.setPrintStatus(1);
            } catch (Exception e) {
                log.error("打印失败");
                printRecord.setPrintStatus(2);
            }

            voucherPrintRecordMapper.updatePrintStatus(printRecord.getId(), 1, null);

            log.info("成功创建打印记录: {}, 券码: {}", code, availableVoucher.getCode());
            return voucherPrintResp;
            
        } finally {
            // 释放锁
            if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
                redisTemplate.delete(lockKey);
            }
        }
    }

    @Override
    public VoucherPrintResp queryPrintedVoucher(Long faceId) {

        FaceEntity face = faceRepository.getFace(faceId);
        if (face == null) {
            throw new BaseException("请上传人脸");
        }

        Long currentUserId = Long.valueOf(BaseContextHandler.getUserId());

        // 验证faceId是否属于当前用户
        validateFaceOwnership(faceId, currentUserId);
        // 检查是否已存在相同的打印记录
        VoucherPrintRecord existingRecord = voucherPrintRecordMapper.selectByFaceBrokerScenic(
                faceId, face.getScenicId());
        if (existingRecord == null) {
            return null;
        }
        return buildResponse(existingRecord);
    }

    @Override
    public VoucherPrintResp printBookingTicket(VoucherPrintReq request) {
        // 参数验证
        if (request.getFaceId() == null) {
            throw new BaseException("用户faceId不能为空");
        }
        if (request.getBrokerId() == null) {
            throw new BaseException("经纪人ID不能为空");
        }
        FaceEntity face = faceRepository.getFace(request.getFaceId());
        if (face == null) {
            throw new BaseException("请上传人脸");
        }
        request.setScenicId(face.getScenicId());

        ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(face.getScenicId());
        if (!Boolean.TRUE.equals(configManager.getBoolean("booking_enable"))) {
            return null;
        }
        Long brokerId = configManager.getLong("booking_broker_id");
        if (brokerId != null) {
            if (!request.getBrokerId().equals(brokerId)) {
                return null;
            }
        }

        Long currentUserId = Long.valueOf(BaseContextHandler.getUserId());

        // 验证faceId是否属于当前用户
        validateFaceOwnership(request.getFaceId(), currentUserId);

        // 使用Redis分布式锁防止重复打印
        String lockKey = String.format(PRINT_LOCK_KEY, request.getFaceId(), request.getBrokerId(), request.getScenicId());
        String lockValue = UUID.randomUUID().toString();

        try {
            // 尝试获取锁，超时时间30秒
            Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);
            if (Boolean.FALSE.equals(lockAcquired)) {
                throw new BaseException("请求处理中，请稍后再试");
            }

            // 检查是否已存在相同的打印记录
            VoucherPrintRecord existingRecord = voucherPrintRecordMapper.selectByFaceBrokerScenic(
                    request.getFaceId(), request.getScenicId());

            if (existingRecord != null) {
                log.info("找到已存在的打印记录，返回该记录: {}", existingRecord.getId());
                return buildResponse(existingRecord);
            }

            // 生成流水号
            String code = generateCode();

            // 创建打印记录
            VoucherPrintRecord printRecord = new VoucherPrintRecord();
            printRecord.setCode(code);
            printRecord.setFaceId(request.getFaceId());
            printRecord.setBrokerId(request.getBrokerId());
            printRecord.setScenicId(request.getScenicId());
            printRecord.setPrintStatus(0); // 待打印
            printRecord.setCreateTime(new Date());
            printRecord.setUpdateTime(new Date());
            printRecord.setDeleted(0);

            voucherPrintRecordMapper.insert(printRecord);
            VoucherPrintResp voucherPrintResp = buildResponse(printRecord);

            try {
                printTicket(printRecord, voucherPrintResp);
                printRecord.setPrintStatus(1);
            } catch (Exception e) {
                log.error("打印失败");
                printRecord.setPrintStatus(2);
            }

            voucherPrintRecordMapper.updatePrintStatus(printRecord.getId(), 1, null);

            log.info("成功创建打印记录: {}, 流水号: {}", code, printRecord.getCode());
            return voucherPrintResp;

        } finally {
            // 释放锁
            if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
                redisTemplate.delete(lockKey);
            }
        }
    }

    /**
     * 根据券码查询voucher_batch_config记录
     * @param voucherCode 券码
     * @return 券码批次配置
     */
    public PriceVoucherBatchConfig getVoucherBatchConfigByCode(String voucherCode) {
        if (voucherCode == null || voucherCode.trim().isEmpty()) {
            throw new BaseException("券码不能为空");
        }
        return priceVoucherBatchConfigMapper.selectByVoucherCode(voucherCode);
    }

    /**
     * 验证faceId是否属于当前用户
     */
    private void validateFaceOwnership(Long faceId, Long currentUserId) {
        FaceEntity face = faceRepository.getFace(faceId);
        if (face == null) {
            throw new BaseException("用户人脸信息不存在");
        }
        
        if (!currentUserId.equals(face.getMemberId())) {
            throw new BaseException("无权限操作该人脸信息");
        }
    }
    
    /**
     * 获取可用且未被打印过的券码
     */
    private PriceVoucherCode getAvailableUnprintedVoucher(Long scenicId) {
        return priceVoucherCodeMapper.findRandomUnprintedVoucher(scenicId);
    }
    
    /**
     * 生成流水号（优化版本）
     * 使用原子计数器确保唯一性，解决原方法在高并发下的重复问题
     */
    private String generateCode() {
        // 方案：使用毫秒级时间戳 + 原子计数器
        // 格式：5位计数器 + SSS
        SimpleDateFormat sdf = new SimpleDateFormat("SSS");
        String timestamp = sdf.format(new Date());
        long count = counter.incrementAndGet() % 100000; // 5位计数，循环使用
        return String.format("%05d", count) + timestamp;
    }
    
    /**
     * 构建响应对象
     */
    private VoucherPrintResp buildResponse(VoucherPrintRecord record) {
        VoucherPrintResp response = new VoucherPrintResp();
        BeanUtils.copyProperties(record, response);
        if (Strings.isNotBlank(record.getVoucherCode())) {
            PriceVoucherBatchConfig priceVoucherBatchConfig = priceVoucherBatchConfigMapper.selectByVoucherCode(record.getVoucherCode());
            if (priceVoucherBatchConfig != null) {
                if (Integer.valueOf(0).equals(priceVoucherBatchConfig.getDiscountType())) {
                    response.setType("赠品兑换码");
                } else {
                    response.setType("抵扣兑换码");
                }
            }
            response.setCode(record.getVoucherCode());
        } else {
            response.setType("小票配对码");
        }
        return response;
    }
    
    /**
     * 调用打印机接口
     */
    private void printTicket(VoucherPrintRecord record, VoucherPrintResp voucherPrintResp) throws Exception {
        FaceEntity face = faceRepository.getFace(record.getFaceId());
        MpConfigEntity scenicMpConfig = scenicRepository.getScenicMpConfig(face.getScenicId());
        String urlLink;
        if (Strings.isNotBlank(record.getVoucherCode())) {
            urlLink = WxMpUtil.generateUrlLink(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), "pages/videoSynthesis/index", "code=" + record.getVoucherCode() + "&scenicId=" + face.getScenicId() + "&faceId=" + face.getId());
            urlLink = urlLink + "?cq=T"+record.getVoucherCode();
            log.info(" 调用打印机打印小票，记录ID: {}, 券码: {}", record.getId(), record.getVoucherCode());
        } else {
            urlLink = WxMpUtil.generateUrlLink(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), "pages/videoSynthesis/index", "scenicId=" + face.getScenicId() + "&faceId=" + face.getId());
            log.info(" 调用打印机打印小票，记录ID: {}, 配对码: {}", record.getId(), record.getCode());
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        SimpleDateFormat sdf2 = new SimpleDateFormat("MM月dd日");
        String content;
        content  = "<BR><B>世界再大</B><BR>";
        content += "<B>你永远是这段旅途</B><BR>";
        content += "<B>的焦点</B><BR>";
        content += "━━━━━━━━━━━━━━━━<BR>";
        content += "正片主演：旅途中最靓的你<BR>";
        content += "拍摄地点：剑门关滑道<BR>";
        content += "拍摄日期：";
        content += sdf.format(new Date());
        content += "<BR>";
        content += "大片内容：包含精剪Vlog、打卡录像<BR>";
        content += "          与精选照片<BR><BR>";
        content += "<QR>"+urlLink+"</QR>";
        content += "<CB>游后微信扫码领取</CB>";
        content += "<C>精彩指数:★★★★★</C><BR>";
        content += "━━━━━━━━━━━━━━━━<BR>";
        content += "<CB>"+voucherPrintResp.getCode()+"</CB>";
        content += "<C>"+voucherPrintResp.getType()+"</C>";
        content += "<C>有效期："+sdf2.format(new Date())+"</C>";
        ScenicConfigManager configManager = scenicRepository.getScenicConfigManager(face.getScenicId());
        if (Strings.isNotBlank(configManager.getString("ticket_print_sn"))) {
            FeiETicketPrinter.doPrint(configManager.getString("ticket_print_sn"), content, 1);
        } else {
            log.warn("打印没有配置->内容：\n{}", content);
        }
    }
}