package org.dromara.zjk.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.zjk.domain.ZjkExpertMoneyInfo;
import org.dromara.zjk.domain.ZjkInvoice;
import org.dromara.zjk.domain.ZjkSettlementVoucher;
import org.dromara.zjk.domain.bo.ZjkInvoiceBo;
import org.dromara.zjk.domain.vo.*;
import org.dromara.zjk.enums.MoneySettlementStatusEnum;
import org.dromara.zjk.expert.expertRecommend.domain.vo.ZjkExpertRecommendVo;
import org.dromara.zjk.mapper.ZjkExpertMoneyConfigMapper;
import org.dromara.zjk.mapper.ZjkExpertMoneyInfoMapper;
import org.dromara.zjk.mapper.ZjkInvoiceMapper;
import org.dromara.zjk.mapper.ZjkSettlementVoucherMapper;
import org.dromara.zjk.service.IZjkInvoiceService;
import org.dromara.zjk.zwy.client.HmacClient;
import org.dromara.zjk.zwy.utils.ObjectHashGenerator;
import org.mapstruct.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 发票Service业务层处理
 *
 * @author mzx
 * @date 2024-12-09
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class ZjkInvoiceServiceImpl implements IZjkInvoiceService {
    @Resource
    private final ZjkInvoiceMapper zjkInvoiceMapper;
    @Resource
    private final ZjkSettlementVoucherMapper zjkSettlementVoucherMapper;
    @Resource
    private ZjkExpertMoneyInfoMapper zjkExpertMoneyInfoMapper;
    @Autowired
    private ZjkExpertMoneyConfigMapper zjkExpertMoneyConfigMapper;

    private final HmacClient hmacClient;

    /**
     * 查询发票
     *
     * @param id 主键
     * @return 发票
     */
    @Override
    public ZjkInvoiceVo queryById(Long id) {
        return zjkInvoiceMapper.selectInfoById(id);
    }

    /**
     * 分页查询发票列表
     *
     * @param bo        查询条件  参数职称等级  联系电话 身份证号 专家名称
     * @param pageQuery 分页参数
     * @return 发票分页列表
     */
    @Override
    public TableDataInfo<ZjkInvoiceVo> queryPageList(ZjkInvoiceBo bo, PageQuery pageQuery) {
        Page<ZjkInvoiceVo> result = zjkInvoiceMapper.queryPageList(pageQuery.build(),bo);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的发票列表
     *
     * @param bo 查询条件
     * @return 发票列表
     */
    @Override
    public List<ZjkInvoiceVo> queryList(ZjkInvoiceBo bo) {
        LambdaQueryWrapper<ZjkInvoice> lqw = buildQueryWrapper(bo);
        return zjkInvoiceMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<ZjkInvoice> buildQueryWrapper(ZjkInvoiceBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<ZjkInvoice> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getInvoiceNumber() != null, ZjkInvoice::getInvoiceNumber, bo.getInvoiceNumber());
        lqw.eq(bo.getInvoiceMoney() != null, ZjkInvoice::getInvoiceMoney, bo.getInvoiceMoney());
        lqw.eq(bo.getStartDate() != null, ZjkInvoice::getStartDate, bo.getStartDate());
        lqw.like(StringUtils.isNotBlank(bo.getExpertName()), ZjkInvoice::getExpertName, bo.getExpertName());
        lqw.eq(StringUtils.isNotBlank(bo.getExpertPhone()), ZjkInvoice::getExpertPhone, bo.getExpertPhone());
        lqw.eq(StringUtils.isNotBlank(bo.getExpertIdNumber()), ZjkInvoice::getExpertIdNumber, bo.getExpertIdNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getExpertTitleLevel()), ZjkInvoice::getExpertTitleLevel, bo.getExpertTitleLevel());
        lqw.eq(StringUtils.isNotBlank(bo.getInvoiceAttached()), ZjkInvoice::getInvoiceAttached, bo.getInvoiceAttached());
        return lqw;
    }

    /**
     * 新增发票
     *
     * @param bo 发票
     * @return 是否新增成功
     */
/*    @Override
    public Boolean insertByBo(ZjkInvoiceBo bo) {
        ZjkInvoice add = MapstructUtils.convert(bo, ZjkInvoice.class);
        validEntityBeforeSave(add);
        add.setCreateBy(LoginHelper.getLoginUser().getUserId());
        add.setCreateTime(new Date());
        add.setCreateDept(LoginHelper.getLoginUser().getDeptId());
        add.setUpdateBy(LoginHelper.getLoginUser().getUserId());
        add.setUpdateTime(new Date());
        boolean flag = zjkInvoiceMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }*/
    @Override
    public Boolean insertByBo(ZjkInvoiceBo bo) {
        ZjkInvoice add = MapstructUtils.convert(bo, ZjkInvoice.class);
        validEntityBeforeSave(add);

        Long userId = LoginHelper.getUserId();
        Long deptId = LoginHelper.getDeptId();
        Date now = new Date();

        // 设置创建/修改人信息
        add.setCreateBy(userId);
        add.setCreateTime(now);
        add.setCreateDept(deptId);
        add.setUpdateBy(userId);
        add.setUpdateTime(now);

        // 插入
        boolean flag = zjkInvoiceMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());

            // 可选：生成 HMAC
            try {
                ZjkInvoiceVo zjkInvoice = zjkInvoiceMapper.selectInfoById(add.getId());
//                ZjkInvoiceVo entity = MapstructUtils.convert(zjkInvoice, ZjkInvoiceVo.class);
                String hash = ObjectHashGenerator.generateHash(zjkInvoice);
                String base64 = Base64.getEncoder().encodeToString(hash.getBytes(StandardCharsets.UTF_8));
                 String hmac = hmacClient.calculateHmac(base64);
//                String hmac = base64;
                add.setHmac(hmac);
                zjkInvoiceMapper.updateById(add); // 回写 HMAC
            } catch (Exception e) {
                log.warn("发票 HMAC 生成失败，ID: {}", add.getId(), e);
            }
        }

        return flag;
    }

    /**
     * 修改发票
     *
     * @param bo 发票
     * @return 是否修改成功
     */
   /* @Override
    public Boolean updateByBo(ZjkInvoiceBo bo) {
        ZjkInvoice update = MapstructUtils.convert(bo, ZjkInvoice.class);
        update.setUpdateBy(LoginHelper.getLoginUser().getUserId());
        update.setUpdateTime(new Date());
        validEntityBeforeSave(update);
        return zjkInvoiceMapper.updateById(update) > 0;
    }*/
    @Override
    public Boolean updateByBo(ZjkInvoiceBo bo) {
        // 转换对象
        ZjkInvoice update = MapstructUtils.convert(bo, ZjkInvoice.class);
        update.setUpdateBy(LoginHelper.getLoginUser().getUserId());
        update.setUpdateTime(new Date());

        // 前置校验
        validEntityBeforeSave(update);

        // 更新主表
        boolean flag = zjkInvoiceMapper.updateById(update) > 0;
        if (flag) {
            try {
                // 重新查询一遍数据（确保包含自动填充字段）
                ZjkInvoiceVo zjkInvoice = zjkInvoiceMapper.selectInfoById(update.getId());
//                ZjkInvoice entity = MapstructUtils.convert(zjkInvoice, ZjkInvoice.class);
                String hash = ObjectHashGenerator.generateHash(zjkInvoice);
                // 生成哈希和 Base64
                String base64 = Base64.getEncoder().encodeToString(hash.getBytes(StandardCharsets.UTF_8));

                // 调用 HMAC 服务（示例中直接用 base64 代替）
                 String hmac = hmacClient.calculateHmac(base64);
//                String hmac = base64;

                update.setHmac(hmac);
                zjkInvoiceMapper.updateById(update); // 二次更新，仅更新 HMAC 字段

                log.info("发票更新成功，HMAC 已刷新，ID: {}", update.getId());
            } catch (Exception e) {
                log.error("更新发票 HMAC 失败，ID: {}", update.getId(), e);
            }
        }

        return flag;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(ZjkInvoice entity){
        //TODO 做一些数据校验,如唯一约束
        //发票号码验证是否已存在
        Long invoiceNumber = entity.getInvoiceNumber();
        LambdaQueryWrapper<ZjkInvoice> zjkInvoiceLambdaQueryWrapper = new LambdaQueryWrapper<>();
        zjkInvoiceLambdaQueryWrapper.eq(ZjkInvoice::getInvoiceNumber, invoiceNumber);
        if (entity.getId()!=null){
            zjkInvoiceLambdaQueryWrapper.ne(ZjkInvoice::getId, entity.getId());
        }
        Long invoiceCount = zjkInvoiceMapper.selectCount(zjkInvoiceLambdaQueryWrapper);
        log.info("invoiceCount:{}", invoiceCount);
        if (invoiceCount > 0){
            throw new ServiceException("已存在发票号为:"+invoiceNumber+"的发票");
        }
    }

    /**
     * 校验并批量删除发票信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return zjkInvoiceMapper.deleteByIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void imports(MultipartFile file) {
        try {
            InputStream inputStream = file.getInputStream();
            // 文件转化
            List<ZjkInvoice> zjkInvoiceList = ExcelUtil.importExcel(inputStream, ZjkInvoice.class);
            if (zjkInvoiceList != null && !zjkInvoiceList.isEmpty()) {
                Set<Long> invoiceNumberList = zjkInvoiceList.stream().map(ZjkInvoice::getInvoiceNumber).collect(Collectors.toSet());;
                List<ZjkInvoice> exitsZjkInvoice = zjkInvoiceMapper.selectList(new LambdaQueryWrapper<ZjkInvoice>().in(ZjkInvoice::getInvoiceNumber,invoiceNumberList));
                if (CollUtil.isNotEmpty(exitsZjkInvoice)) {
                    String existingInvoiceNumbersStr = exitsZjkInvoice.stream()
                        .map(ZjkInvoice::getInvoiceNumber)
                        .map(String::valueOf)
                        .collect(Collectors.joining(","));
                    throw new ServiceException("以下发票号已存在: " + existingInvoiceNumbersStr + "，请修改后重新导入");
                }
                //处理职称等级
                //得到职称-数字一一对应list
                List<SelectOptionVo> expertMoneyConfigList = zjkExpertMoneyConfigMapper.getExpertMoneyConfig();
                if (expertMoneyConfigList != null && !expertMoneyConfigList.isEmpty()) {
                    for (ZjkInvoice zjkInvoice : zjkInvoiceList) {
                        //因为数据库职称是数字  转换成汉字
                        for (SelectOptionVo selectOptionVo : expertMoneyConfigList) {
                            if (selectOptionVo.getLabel().equals(zjkInvoice.getExpertTitleLevel())) {
                                zjkInvoice.setExpertTitleLevel(selectOptionVo.getValue());//赋予汉字职称
                            }
                        }
                    }
                }

                //insert
                zjkInvoiceMapper.insert(zjkInvoiceList);
            }
        }catch (IOException e){
            log.error("导入发票信息失败,{}",e.getMessage());
            throw new ServiceException("导入发票信息失败,请联系管理员");
        }
    }

    @Override
    public InvoiceStatisticsVO statistics() {
        InvoiceStatisticsVO invoiceStatisticsVO = new InvoiceStatisticsVO();
        //发票总数
        Long count = zjkInvoiceMapper.selectCount(null);
        invoiceStatisticsVO.setCount(count.intValue());
        //总金额
        BigDecimal totalMoney = zjkInvoiceMapper.selectTotalMoney();
        invoiceStatisticsVO.setTotalMoney(totalMoney);
        //开票单位及其个数  赋值vo
        List<UnitVO> unitVOList = new ArrayList<>();
        /*Set<String> unitSet =  zjkInvoiceMapper.selectAllUnit();
        //遍历得到单位个数
        for (String unit :unitSet){
            LambdaQueryWrapper<ZjkInvoice> zjkInvoiceLambdaQueryWrapper = new LambdaQueryWrapper<>();
            zjkInvoiceLambdaQueryWrapper.eq(ZjkInvoice::getInvoiceUnit, unit);
            Long unitCount = zjkInvoiceMapper.selectCount(zjkInvoiceLambdaQueryWrapper);
            UnitVO unitVO = new UnitVO();
            unitVO.setName(unit);
            unitVO.setCount(unitCount.intValue());
            unitVOList.add(unitVO);
        }
        invoiceStatisticsVO.setUnitList(unitVOList);*/
        return invoiceStatisticsVO;
    }

    @Override
    public Boolean check(Long moneyId) {
        ZjkInvoice zjkInvoice = zjkInvoiceMapper.selectById(moneyId);
        zjkInvoice.setCheckStatus(1);
        zjkInvoiceMapper.updateById(zjkInvoice);
        //TODO 向【财务报表和结算凭证】同步该数据
        ZjkSettlementVoucher settlementVoucher = new ZjkSettlementVoucher();
        // 生成报表编号：FP-20250731-YDTJ
        String dateStr = DateUtil.format(new Date(), "yyyyMMdd");
        String pzCode = "pz-" + dateStr;
        settlementVoucher.setVoucherCode(pzCode);
        settlementVoucher.setProductId(zjkInvoice.getProductId());
        settlementVoucher.setExpertId(zjkInvoice.getExpertName()==null?null:Long.valueOf(zjkInvoice.getExpertName()));
        settlementVoucher.setSettleNo(zjkExpertMoneyInfoMapper.selectById(zjkInvoice.getSettleId()).getSettlementSn());
        settlementVoucher.setMoney(zjkExpertMoneyConfigMapper.selectById(zjkInvoice.getExpertTitleLevel()).getMoneyNumber());
        settlementVoucher.setSyncStatus(1);
        zjkSettlementVoucherMapper.insert(settlementVoucher);
        return true;
    }

    @Override
    public CheckSupportVo getInvByProductInfo(String productId, String reviewSn, String expertId) {

        LambdaQueryWrapper<ZjkExpertMoneyInfo> wq = new LambdaQueryWrapper<>();
        wq.eq(ZjkExpertMoneyInfo::getProductId, productId)
            .eq(ZjkExpertMoneyInfo::getProductType, reviewSn)
            .eq(ZjkExpertMoneyInfo::getExpertId, expertId);
        ZjkExpertMoneyInfo zjkExpertMoneyInfo = zjkExpertMoneyInfoMapper.selectOne(wq);
        CheckSupportVo checkSupportVo = new CheckSupportVo();
        if(zjkExpertMoneyInfo!=null){
            checkSupportVo.setExpertTypeMoneyFee(zjkExpertMoneyInfo.getExpertTypeMoneyFee());
            checkSupportVo.setExpertMoney(zjkExpertMoneyInfo.getExpertMoney());
            //结算后通过结算单号获取发票信息
            if(zjkExpertMoneyInfo.getStatus().equals(MoneySettlementStatusEnum.NO_SETTLEMENT.getCode())){
                LambdaQueryWrapper<ZjkInvoice> invWq = new LambdaQueryWrapper<>();
                invWq.eq(ZjkInvoice::getSettleId, zjkExpertMoneyInfo.getMoneyId()).orderByDesc(ZjkInvoice::getStartDate);
                List<ZjkInvoice> zjkInvoices = zjkInvoiceMapper.selectList(invWq);
                if(!CollectionUtil.isEmpty(zjkInvoices)){
                    //有发票信息，已开票，求出发票总额
                    checkSupportVo.setInvStu("已开票");
                    BigDecimal reduce = zjkInvoices.stream().map(ZjkInvoice::getInvoiceMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
                    checkSupportVo.setInvoiceMoney(reduce);
                    checkSupportVo.setInvFileId(zjkInvoices.get(0).getInvoiceAttached());
                    List<String> fids = zjkInvoices.stream().map(ZjkInvoice::getInvoiceAttached).collect(Collectors.toList());
                    checkSupportVo.setFids(fids);
                }
            }
        }else{
            checkSupportVo.setInvStu("未开票");
        }
        return checkSupportVo;
    }
}
