
package com.yami.shop.service.impl;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.model.*;
import com.yami.shop.bean.po.EnterpriseUserPo;
import com.yami.shop.bean.vo.EnterpriseUserVo;
import com.yami.shop.bean.vo.PointsRechargeVO;
import com.yami.shop.common.exception.GlobalException;
import com.yami.shop.common.util.CommonUtils;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.dao.*;
import com.yami.shop.service.PointsFailureRecordService;
import com.yami.shop.service.PointsRechargeService;
import com.yami.shop.service.PointsRecordService;
import com.yami.shop.utils.ExportUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 积分充值 Service实现类
 *
 * @author fbj
 * @since 2025-09-XX
 */
@Service
@AllArgsConstructor
@Slf4j
public class PointsRechargeServiceImpl extends ServiceImpl<PointsRechargeMapper, PointsRecharge> implements PointsRechargeService {

    private final PointsRechargeMapper pointsRechargeMapper;
    private final PointsRecordMapper pointsRecordMapper;
    private final PointsFailureRecordService pointsFailureRecordService;
    private final UserMapper userMapper;
    private final ChannelMapper channelMapper;
    private final PointsRecordService pointsRecordService;

    @Override
    public void exportTemplate(HttpServletResponse response) throws Exception {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode("积分充值导入模板", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        // 创建示例数据
        List<com.yami.shop.service.impl.PointsRechargeTemplateDTO> templateData = new ArrayList<>();
        // 使用EasyExcel导出模板
        EasyExcel.write(response.getOutputStream(), com.yami.shop.service.impl.PointsRechargeTemplateDTO.class)
                .sheet("积分充值导入模板")
                .doWrite(templateData);
    }


    @Override
    public String importPointsRecharge(MultipartFile file, Long userId) throws Exception {
        // 读取Excel数据
        List<com.yami.shop.service.impl.PointsRechargeTemplateDTO> importData = null;
        try {
            importData = EasyExcel.read(file.getInputStream()).head(com.yami.shop.service.impl.PointsRechargeTemplateDTO.class).sheet().doReadSync();
        } catch (IOException e) {
            log.info("导入失败，解析模板有误");
            throw new GlobalException("导入失败，解析模板有误");
        }

        int successCount = 0;
        int failCount = 0;
        String code = CommonUtils.getOrderNo("JF");
        //回去当前登录用户信息


        for (int i = 0; i < importData.size(); i++) {
            com.yami.shop.service.impl.PointsRechargeTemplateDTO templateDTO = importData.get(i);
            try {
                // 数据校验
                CommonUtils.validateCondition(templateDTO.getChannelName() == null,"所属企业不能为空");
                //判断是否存在对应渠道商
                Channel channel = channelMapper.selectOne(new LambdaQueryWrapper<Channel>()
                        .eq(Channel::getChannelName, templateDTO.getChannelName().trim()));
                CommonUtils.validateCondition(channel == null,"渠道商不存在");
                String userName = templateDTO.getUserName();
                CommonUtils.validateCondition(userName == null,"员工名称不能为空");
                CommonUtils.validateCondition(templateDTO.getPoints() == null,"充值积分不能为空");
                CommonUtils.validateCondition(templateDTO.getExpiryDate() == null,"过期时间不能为空");

                // 构造实体对象
                PointsRecharge pointsRecharge = new PointsRecharge();
                String userPhone = templateDTO.getUserPhone();
                CommonUtils.validateCondition(userPhone == null,"员工电话号码不能为空");

                userName = userName.trim();
                userPhone = userPhone.trim();
                //电话号码格式验证
                CommonUtils.validateCondition(!userPhone.matches("^(1(3[0-9]|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8})$"),"手机号码格式错误");

                //通过用户名称和电话号码校验员工是否存在（存在用户绑定用户，不存在正常添加数据）
                User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                        .eq(User::getUserMobile, userPhone)
                        .eq(User::getStatus, 1)
                        .last("LIMIT 1"));

                CommonUtils.validateCondition(user == null,"员工手机号码不存在");
                pointsRecharge.setUserId(user.getUserId());
                CommonUtils.validateCondition(!userName.equals(user.getRealName()),"员工姓名不正确！");
                CommonUtils.validateCondition(user.getChannelId() == null,"员工未绑定企业，不能导入积分！");
                //小程序注册未绑定企业没有绑定企业可以正常添加，绑定了必须相同
                // 更安全的写法
                CommonUtils.validateCondition(user.getChannelId() == null || !channel.getId().equals(user.getChannelId().longValue()), "该员工属于企业（"+channel.getChannelName()+"），所属企业不匹配！");


                pointsRecharge.setChannelId(channel.getId());
                pointsRecharge.setUserName(userName);
                pointsRecharge.setUserPhone(templateDTO.getUserPhone());
                pointsRecharge.setChannelName(templateDTO.getChannelName());
                pointsRecharge.setPoints(templateDTO.getPoints());
                pointsRecharge.setExpiryDate(templateDTO.getExpiryDate());
                pointsRecharge.setType(0);
                pointsRecharge.setCreateTime(new Date());
                pointsRecharge.setUpdateTime(new Date());
                pointsRecharge.setRechargeStatus(1);
                pointsRecharge.setCode(code);
                pointsRecharge.setCreateBy(userId);

                // 生成充值单号
                pointsRecharge.setRechargeNumber(CommonUtils.getOrderNo("CZ"));

                // 保存数据（因为有过期时间每次导入的都是新增）
                log.info("保存数据：{}", pointsRecharge);
                this.save(pointsRecharge);
                log.info("保存数据成功：{}", pointsRecharge);
                // 添加积分记录明细
                PointsRecord pointsRecord = new PointsRecord();
                pointsRecord.setUserId(pointsRecharge.getUserId());
                pointsRecord.setChannelId(pointsRecharge.getChannelId());
                pointsRecord.setPointsId(pointsRecharge.getId());
                pointsRecord.setCode(pointsRecharge.getCode());
                pointsRecord.setPoints(pointsRecharge.getPoints());
                pointsRecord.setPointsType(1);
                pointsRecord.setPointsAudit(1);
                pointsRecord.setCreationDate(new Date());
                pointsRecord.setExpiryDate(pointsRecharge.getExpiryDate());

                Long aLong = pointsRecordMapper.statisticsPoint(pointsRecord.getUserId(), pointsRecord.getChannelId());
                pointsRecord.setCurrentlyAvailablePoints(pointsRecord.getPoints().add(BigDecimal.valueOf(aLong == null ? 0 : aLong )));

                pointsRecordService.save(pointsRecord);
                successCount++;
                addImportRecord(1, "成功", templateDTO, code,userId);
            } catch (GlobalException e) {
                log.info("导入失败：{}", e.getMessage());
                failCount++;
                addImportRecord(0, e.getMessage(), templateDTO, code,userId);
            } catch (Exception e) {
                failCount++;
                addImportRecord(0, "系统异常，请联系管理员", templateDTO, code,userId);
            }
        }

        if (successCount > 0 && failCount == 0) {
            return String.format("导入完成，全部成功：%d条", successCount);
        } else if (successCount == 0 && failCount > 0) {
            return String.format("导入完成，全部失败：%d条", failCount);
        } else {
            return String.format("导入完成，成功：%d条，失败：%d条", successCount, failCount);
        }
    }




    /**
     * 封装新增导入记录方法
     *
     * @param status  状态
     * @param message 成功还是失败原因
     */
    private void addImportRecord(int status, String message, com.yami.shop.service.impl.PointsRechargeTemplateDTO templateDTO, String code,Long userId) {
        // 数据保存到积分充值失败记录表
        PointsFailureRecord failureRecord = new PointsFailureRecord();
        failureRecord.setCode(code);
        failureRecord.setRechargeStatus(status);
        failureRecord.setReasonForFailure(message);
        failureRecord.setChannelName(templateDTO.getChannelName());
        failureRecord.setUserName(templateDTO.getUserName());
        failureRecord.setUserPhone(templateDTO.getUserPhone());
        failureRecord.setPoints(templateDTO.getPoints());
        failureRecord.setExpiryDate(templateDTO.getExpiryDate());
        failureRecord.setCreateTime(new Date());
        failureRecord.setCreateBy(userId);

        // 保存记录
        pointsFailureRecordService.save(failureRecord);
    }

    @Override
    public IPage<PointsRechargeVO> statisticsList(PointsRecharge pointsRecharge) {
        List<PointsRechargeVO> pointsRechargeVOS = pointsRechargeMapper.statisticsList(pointsRecharge);
        IPage<PointsRechargeVO> page = new Page<>(0, 1000);
        page.setRecords(pointsRechargeVOS);
        page.setTotal(pointsRechargeVOS.size());

        return  page;
    }

    /**
     * 导出积分
     * @param pointsRecharge
     * @param response
     */
    @Override
    public void export(PointsRecharge pointsRecharge, HttpServletResponse response) {
        List<PointsRecharge> enterpriseUserVos= pointsRechargeMapper.findList(pointsRecharge);
        if (enterpriseUserVos.isEmpty()){
            throw new GlobalException("该次导出未查询到数据,不允许导出");
        }
        exportNormalOrders(enterpriseUserVos,response,"积分列表");
    }

    @Override
    public IPage<PointsRecharge> findPage(PageParam<PointsRecharge> pageParam, PointsRecharge pointsRecharge) {
        return pointsRechargeMapper.findPage(pageParam,pointsRecharge);
    }

    /**
     * 导出正常订单数据到Excel（直接输出到HttpServletResponse）
     */
    public void exportNormalOrders(List<PointsRecharge> orderList,
                                   HttpServletResponse response,
                                   String fileName) {
        // 设置响应头
        ExportUtils.setupResponse(response, fileName);

        try (Workbook workbook = new XSSFWorkbook()) {
            // 创建Sheet
            Sheet sheet = workbook.createSheet("积分列表");

            // 设置列宽
            setupColumnWidth(sheet);

            // 创建表头样式
            CellStyle headerStyle = ExportUtils.createHeaderStyle(workbook);

            // 创建数据样式
            CellStyle dataStyle = ExportUtils.createDataStyle(workbook);
            CellStyle numberStyle = ExportUtils.createNumberStyle(workbook);
            CellStyle dateStyle = ExportUtils.createDateStyle(workbook);


            String[] headers = {
                    "序号","所属企业", "员工姓名", "员工手机号", "充值积分", "过期时间",
                    "创建时间"
            };
            // 创建合并单元格的表头
            ExportUtils.createMergedHeader(sheet, headerStyle,"积分列表",6);

            // 创建列标题行
            ExportUtils.createColumnHeaders(sheet, headerStyle,headers);

            // 填充数据
            fillOrderData(sheet, orderList, dataStyle, numberStyle, dateStyle);

            // 写入响应流
            workbook.write(response.getOutputStream());
            response.getOutputStream().flush();

            log.info("订单导出成功，文件：{}，记录数：{}", fileName, orderList.size());

        } catch (Exception e) {
            log.error("订单导出失败", e);
            throw new RuntimeException("导出失败：" + e.getMessage());
        }
    }

    /**
     * 设置列宽
     */
    private void setupColumnWidth(Sheet sheet) {
        // 根据模板设置列宽（单位：1/256字符宽度）
        sheet.setColumnWidth(0, 12 * 256);   // 序号
        sheet.setColumnWidth(1, 12 * 256);   // 所属企业
        sheet.setColumnWidth(2, 18 * 256);   // 员工姓名
        sheet.setColumnWidth(3, 18 * 256);   // 员工手机号
        sheet.setColumnWidth(4, 10 * 256);   // 充值积分
        sheet.setColumnWidth(5, 8 * 256);    // 过期时间
        sheet.setColumnWidth(6, 25 * 256);   // 创建时间
    }

    /**
     * 填充订单数据
     */
    private void fillOrderData(Sheet sheet, List<PointsRecharge> orderList,
                               CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle) {
        int rowNum = 2; // 从第3行开始（0-based索引）
        int indexNum= 1; //序号
        for (PointsRecharge enterpriseUserVo : orderList) {
            Row row = sheet.createRow(rowNum++);
            row.setHeightInPoints(18);
            // 序号
            ExportUtils.createCell(sheet,row, 0,0,0, indexNum, dataStyle);
            fillOrderRowData(sheet,row, enterpriseUserVo, dataStyle, numberStyle, dateStyle);
            indexNum++;
        }

    }

    /**
     * 填充单行订单数据
     */
    private void fillOrderRowData(Sheet sheet,Row row, PointsRecharge orderRefundVo,
                                  CellStyle dataStyle, CellStyle numberStyle, CellStyle dateStyle) {
        // 所属企业
        ExportUtils.createCell(sheet,row, 1,0,0, orderRefundVo.getChannelName(), dataStyle);

        // 员工姓名
        ExportUtils.createCell(sheet,row, 2,0,0,  orderRefundVo.getUserName(), dateStyle);

        // 员工手机号
        ExportUtils.createCell(sheet,row, 3,0,0, orderRefundVo.getUserPhone(), dateStyle);

        // 充值积分
        ExportUtils.createCell(sheet,row, 4,0,0,orderRefundVo.getPoints(), numberStyle);

        // 过期时间
        ExportUtils.createCell(sheet,row, 5,0,0, ExportUtils.formatDate(orderRefundVo.getExpiryDate()), dateStyle);
        // 创建时间
        ExportUtils.createCell(sheet,row, 6,0,0, ExportUtils.formatDate(orderRefundVo.getCreateTime()), dateStyle);

    }
}
