package com.cf.imes.module.system.service.funds.invoice;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.Assert.AssertUtils;
import com.cf.imes.framework.common.util.date.LocalDateTimeUtils;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.infra.api.file.FileApi;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountAndAvailablePageReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountRespVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceConfirmReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceDetailsPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceDetailsRespVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoicePurchaseRecord;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceRecordsReqVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceRecordsRespVO;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceTitleInfo;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceTitleSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.invoice.InvoiceRecordsDO;
import com.cf.imes.module.system.dal.dataobject.funds.invoice.InvoiceTitleInfoDO;
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO;
import com.cf.imes.module.system.dal.mysql.funds.invoice.InvoiceRecordsMapper;
import com.cf.imes.module.system.dal.mysql.funds.invoice.InvoiceTitleInfoMapper;
import com.cf.imes.module.system.dal.mysql.funds.purchase.PurchaseRecordMapper;
import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum;
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
import com.cf.imes.module.system.service.organ.OrganService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.framework.common.util.json.JsonUtils.parseObject;
import static com.cf.imes.framework.common.util.json.JsonUtils.toJsonString;
import static com.cf.imes.framework.common.util.json.JsonUtils.unzipString;
import static com.cf.imes.framework.common.util.json.JsonUtils.zipString;
import static com.cf.imes.framework.security.core.util.SecurityFrameworkUtils.getUserOrganId;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_NO_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICE_TITLE_NOT_EXISTS_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.INVOICING_REJECT_REASON;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_INVOICE_AGREE_FILE_NULL_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_INVOICE_PURCHASE_RECORD_UPDATE_NUM_NOT_MATCH_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PURCHASE_RECORD_NO_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.THIS_INVOICE_IS_FAIL;


/**
 * @author 发票管理接口 实现类
 */
@Slf4j
@Service
public class InvoiceServiceImpl implements InvoiceService {


    @Resource
    private InvoiceTitleInfoMapper invoiceTitleInfoMapper;

    @Resource
    private PurchaseRecordMapper purchaseRecordMapper;

    @Resource
    private InvoiceRecordsMapper invoiceRecordsMapper;

    @Resource
    private OrganService organService;

    @Resource
    private FileApi fileApi;

    @Override
    public void insetInvoiceTitle(InvoiceTitleSaveReqVO reqVO) {

        if (reqVO.getId() != null) {
            validTitleOrg(reqVO.getId(), reqVO.getOrganId());
            // 更新抬头
            InvoiceTitleInfoDO titleInfoDO = BeanUtils.toBean(reqVO, InvoiceTitleInfoDO.class);
            invoiceTitleInfoMapper.updateById(titleInfoDO);
            return;
        }

        // 新增抬头
        invoiceTitleInfoMapper.insert(InvoiceTitleInfoDO.builder()
                .organId(reqVO.getOrganId())
                .invoiceTitle(reqVO.getInvoiceTitle())
                .invoiceType(reqVO.getInvoiceType())
                .issueType(reqVO.getIssueType())
                .taxpayerId(reqVO.getTaxpayerId())
                .bankName(reqVO.getBankName())
                .bankAccount(reqVO.getBankAccount())
                .registeredAddress(reqVO.getRegisteredAddress())
                .fixedPhone(reqVO.getFixedPhone())
                .email(reqVO.getEmail())
                .build());
    }

    /**
     * 校验抬头组织
     *
     * @param id
     * @param organId
     */
    private void validTitleOrg(Long id, Long organId) {
        InvoiceTitleInfoDO titleInfoDO = invoiceTitleInfoMapper.selectById(id);
        // 抬头不存在
        AssertUtils.notEmpty(titleInfoDO, INVOICE_TITLE_NOT_EXISTS_ERROR);
        // 抬头组织不匹配
        AssertUtils.notEquals(titleInfoDO.getOrganId(), organId, INVOICE_TITLE_NOT_EXISTS_ERROR);
    }

    @Override
    public InvoiceAmountRespVO getInvoiceAmount() {
        return purchaseRecordMapper.getOrgInvoiceAmountStatistics(SecurityFrameworkUtils.getUserOrganId());
    }

    @Override
    public InvoiceAmountRespVO getManageInvoiceAmount() {
        return invoiceRecordsMapper.getManageInvoiceAmountStatistics();
    }

    @Override
    public PageResult<PurchaseRecordDO> getAvailablePage(InvoiceAmountAndAvailablePageReqVO invoiceAmountAndAvailablePageReqVO) {
       return purchaseRecordMapper.selectPage(invoiceAmountAndAvailablePageReqVO);
    }

    @Override
    public void applyInvoice(InvoiceRecordsReqVO reqVO) {
        Long organId = SecurityFrameworkUtils.getUserOrganId();
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        List<Long> purchaseRecordId = reqVO.getPurchaseRecordId();
        // 查询发票抬头
        InvoiceTitleInfoDO invoiceTitleInfoDO = invoiceTitleInfoMapper.selectOne(new LambdaQueryWrapper<InvoiceTitleInfoDO>()
                .eq(InvoiceTitleInfoDO::getOrganId, organId));

        AssertUtils.notEmpty(invoiceTitleInfoDO, INVOICE_TITLE_NOT_EXISTS_ERROR);

        // 查询可开票金额
        BigDecimal invoicableAmount = purchaseRecordMapper.getInvoicableAmount(organId, purchaseRecordId);

        // 更新购买记录表的发票状态
        int update = purchaseRecordMapper.update(new LambdaUpdateWrapper<PurchaseRecordDO>()
                .eq(PurchaseRecordDO::getOrganId, organId)
                .eq(PurchaseRecordDO::getDeleted, false)
                .in(PurchaseRecordDO::getId, purchaseRecordId)
                .set(PurchaseRecordDO::getIsInvocing, InvoiceStatusEnum.PENDINGINVOICING.getCode()));
        if (update != purchaseRecordId.size()) {
            throw new ServiceException(ORG_INVOICE_PURCHASE_RECORD_UPDATE_NUM_NOT_MATCH_ERROR);
        }

        // 查询组织名称
        OrganizationDO organizationDO = organService.validOrgan(organId);

        // 插入开票记录
        invoiceRecordsMapper.insert(InvoiceRecordsDO.builder()
                .userId(loginUser.getId())
                .organName(organizationDO.getName())
                .organId(organId)
                .purchaseIds(JsonUtils.toJsonString(purchaseRecordId))
                .invoiceAmount(invoicableAmount)
                .invoiceTitleId(invoiceTitleInfoDO.getId())
                .invoiceTitleInfo(zipString(toJsonString(invoiceTitleInfoDO)))
                .status(InvoiceStatusEnum.PENDINGINVOICING.getCode())
                .build());
    }

    @Override
    public PageResult<InvoiceRecordsRespVO> getInvoiceRecords(InvoiceDetailsPageReqVO pageReqVO) {
        // 时间类型规范为开始0点，结束12点
        pageReqVO.setCreateTime(LocalDateTimeUtils.generateTimeSection(pageReqVO.getCreateTime()));
        pageReqVO.setInvoiceTime(LocalDateTimeUtils.generateTimeSection(pageReqVO.getInvoiceTime()));

        // 分页查询
        PageResult<InvoiceRecordsDO> pageResult = invoiceRecordsMapper.selectInvoiceRecordsPage(pageReqVO);
        List<InvoiceRecordsDO> resultList = pageResult.getList();

        if(CollUtil.isEmpty(resultList)){
            return new PageResult<>();
        }
        List<InvoiceRecordsRespVO> recordsRespVOS = BeanUtils.toBean(resultList, InvoiceRecordsRespVO.class);
        return new PageResult<>(recordsRespVOS, pageResult.getTotal());
    }

    @Override
    public InvoiceDetailsRespVO getInvoiceRecordDetail(Long recordId) {
        InvoiceRecordsDO invoiceRecordsDO = invoiceRecordsMapper.selectById(recordId);
        AssertUtils.notEmpty(invoiceRecordsDO,INVOICE_NO_EXIST);

        // 记录中的购买记录id数组转list
        String purchaseIds = Optional.ofNullable(invoiceRecordsDO.getPurchaseIds()).orElse("[]");
        List<Long> purchaseIdList = JSON.parseArray(purchaseIds).toJavaList(Long.class).stream().distinct().toList();

        // 查询关联的购买记录
        List<PurchaseRecordDO> purchaseRecordDOS = purchaseRecordMapper.selectList(new LambdaQueryWrapper<PurchaseRecordDO>()
                .eq(PurchaseRecordDO::getOrganId, getUserOrganId())
                .in(PurchaseRecordDO::getId, purchaseIdList));

        List<InvoicePurchaseRecord> invoicePurchaseRecords = new ArrayList<>();
        if (CollUtil.isNotEmpty(purchaseRecordDOS)) {
            for (PurchaseRecordDO purchaseRecordDO : purchaseRecordDOS) {
                InvoicePurchaseRecord invoicePurchaseRecord = new InvoicePurchaseRecord();
                invoicePurchaseRecord.setPurchaseRecordId(purchaseRecordDO.getId());
                invoicePurchaseRecord.setProductName(purchaseRecordDO.getProductName());
                invoicePurchaseRecord.setTradeType(TradeTypeEnum.PRODUCT.getCode());
                invoicePurchaseRecord.setInvoicableAmount(purchaseRecordDO.getTotalAmount().subtract(purchaseRecordDO.getGiftMoneySpent()));
                invoicePurchaseRecord.setCreateTime(purchaseRecordDO.getCreateTime());
                invoicePurchaseRecords.add(invoicePurchaseRecord);
            }
        }

        // 解压发票抬头
        InvoiceTitleInfo invoiceTitleInfo = parseObject(unzipString(invoiceRecordsDO.getInvoiceTitleInfo()), InvoiceTitleInfo.class);

        return InvoiceDetailsRespVO.builder()
                .invoicePurchaseRecords(invoicePurchaseRecords)
                .invoiceTitleInfo(invoiceTitleInfo)
                .applicant(invoiceRecordsDO.getCreator())
                .orgName(invoiceRecordsDO.getOrganName())
                .applyTime(invoiceRecordsDO.getCreateTime())
                .build();
    }

    @Override
    public void confirmInvoice(InvoiceConfirmReqVO reqVO, MultipartFile file) throws IOException {
        // 校验发票申请记录是否存在
        InvoiceRecordsDO invoiceRecordsDO = validateInvoiceExists(reqVO.getId());

        if (invoiceRecordsDO.getStatus().equals(InvoiceStatusEnum.INVOICINGFAILED.getCode())
                || invoiceRecordsDO.getStatus().equals(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode())) {
            throw exception(THIS_INVOICE_IS_FAIL);
        }

        // 校验发票记录下的购买记录是否存在
        String purchaseIds = Optional.ofNullable(invoiceRecordsDO.getPurchaseIds()).orElse("[]");
        List<Long> purchaseIdList = JSON.parseArray(purchaseIds).toJavaList(Long.class).stream().distinct().toList();
        List<PurchaseRecordDO> purchaseRecordDOS = validatePurchaseRecordExists(purchaseIdList);

        if (reqVO.isAgree()) {
            // 同意开票
            // 更新发票申请记录状态
            invoiceRecordsDO.setStatus(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode());
            invoiceRecordsDO.setRemark(reqVO.getRemark());
            // 更新购买记录状态
            purchaseRecordDOS.forEach(f -> f.setIsInvocing(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode()));

            //  发票文件入库
            if (ObjectUtil.isNull(file) || file.isEmpty()) {
                throw new ServiceException(ORG_INVOICE_AGREE_FILE_NULL_ERROR);
            }
            String filePath = fileApi.createFile(IoUtil.readBytes(file.getInputStream()), file.getOriginalFilename());
            invoiceRecordsDO.setInvoiceAttachmentPath(filePath);
            invoiceRecordsDO.setInvoiceNo(reqVO.getInvoiceNo());
        } else {
            // 拒绝开票
            // 更新发票申请记录状态
            invoiceRecordsDO.setStatus(InvoiceStatusEnum.INVOICINGFAILED.getCode());
            String remark = Optional.ofNullable(reqVO.getRemark()).orElseThrow(() -> new ServiceException(INVOICING_REJECT_REASON));
            invoiceRecordsDO.setRemark(remark);

            // 更新购买记录状态
            purchaseRecordDOS.forEach(f -> f.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()));

        }

        invoiceRecordsMapper.updateById(invoiceRecordsDO);

        purchaseRecordMapper.updateBatch(purchaseRecordDOS);
    }

    /**
     * 校验发票申请记录
     *
     * @param invoiceId
     * @return
     */
    private InvoiceRecordsDO validateInvoiceExists(Long invoiceId) {
        InvoiceRecordsDO invoiceRecordsDO = invoiceRecordsMapper.selectById(invoiceId);
        if (ObjectUtil.isNull(invoiceRecordsDO)) {
            throw exception(INVOICE_NO_EXIST);
        }
        return invoiceRecordsDO;
    }

    /**
     * 校验发票记录下的购买记录
     *
     * @param purchaseId
     * @return
     */
    private List<PurchaseRecordDO> validatePurchaseRecordExists(List<Long> purchaseId) {
        List<PurchaseRecordDO> purchaseRecordDOS = purchaseRecordMapper.selectBatchIds(purchaseId);
        if (CollUtil.isEmpty(purchaseRecordDOS)) {
            throw exception(PURCHASE_RECORD_NO_EXIST);
        }
        return purchaseRecordDOS;
    }
}
