package cn.iocoder.yudao.module.system.service.biz;

import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ElderlyConsumerVouchersPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ElderlyConsumerVouchersSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.excel.ElderlyConsumerVouchersImportExcelVO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
import cn.iocoder.yudao.module.system.enums.change.BusinessConstants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Date;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exceptionCustomMsg;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.COMMON_ERROR;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.COMMON_NOT_FOUND;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.EXPENSE_ORDER_ALREADY_CONFIRMED;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.EXPENSE_ORDER_ALREADY_LOCK;

@Service
@Validated
public class ElderlyConsumerVouchersServiceImpl implements ElderlyConsumerVouchersService {

    @Resource
    private ElderlyConsumerVouchersMapper elderlyConsumerVouchersMapper;

    @Resource
    private ExpenseOrderMapper expenseOrderMapper;

    @Resource
    private ExpenseOrderItemMapper expenseOrderItemMapper;

    @Resource
    private ElderlyInfoMapper elderlyInfoMapper;

    @Autowired
    private RefundSettlementOrderMapper refundSettlementOrderMapper;

    @Autowired
    private RefundSettlementOrderItemMapper refundSettlementOrderItemMapper;

    @Override
    public Long create(ElderlyConsumerVouchersDO createReqVO) {
        validateBillingMonth(createReqVO.getBillingMonth());
        elderlyConsumerVouchersMapper.insert(createReqVO);
        updateExpenseOrder(createReqVO);
        updateRefundOrder(createReqVO);
        return createReqVO.getId();
    }

    @Override
    public void update(ElderlyConsumerVouchersSaveReqVO updateReqVO) {
        validateExists(updateReqVO.getId());
        validateBillingMonth(updateReqVO.getBillingMonth());
        ElderlyConsumerVouchersDO data = BeanUtils.toBean(updateReqVO, ElderlyConsumerVouchersDO.class);
        elderlyConsumerVouchersMapper.updateById(data);
    }

    @Override
    @Transactional
    public void delete(Long id) {
        validateExists(id);
        ExpenseOrderItemDO expenseOrderItemDO = expenseOrderItemMapper.selectOne(new LambdaQueryWrapperX<ExpenseOrderItemDO>().eq(ExpenseOrderItemDO::getSourceExpenseItemId, id));
        if(expenseOrderItemDO != null){
            expenseOrderItemMapper.deleteById(expenseOrderItemDO);
            ExpenseOrderDO expenseOrderDO = expenseOrderMapper.selectById(expenseOrderItemDO.getExpenseOrderId());
            if(expenseOrderDO != null){
                expenseOrderDO.setActualAmount(expenseOrderDO.getActualAmount().subtract(expenseOrderItemDO.getTotalAmount()));
                expenseOrderMapper.updateById(expenseOrderDO);
            }
        }
        elderlyConsumerVouchersMapper.deleteById(id);
        deleteRefundOrderItem(id);
    }

    @Override
    public ElderlyConsumerVouchersDO get(Long id) {
        return elderlyConsumerVouchersMapper.selectByIdWithElderInfo(id);
    }

    @Override
    public PageResult<ElderlyConsumerVouchersDO> getPage(ElderlyConsumerVouchersPageReqVO pageReqVO) {
        validateBillingMonth(pageReqVO.getBillingMonth());
        Page<ElderlyConsumerVouchersDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<ElderlyConsumerVouchersDO> list = elderlyConsumerVouchersMapper.selectPageWithElderInfo(page, pageReqVO);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    @Transactional
    public void importData(List<ElderlyConsumerVouchersImportExcelVO> list) {
        if (list == null || list.isEmpty()) {
            throw exceptionCustomMsg(COMMON_ERROR, "导入数据不能为空");
        }
        Long currentTenantId = TenantContextHolder.getTenantId();
        for (int i = 0; i < list.size(); i++) {
            int excelRow = i + 2;
            ElderlyConsumerVouchersImportExcelVO item = list.get(i);
            if (item == null) {
                throw exceptionCustomMsg(COMMON_ERROR, "第" + excelRow + "行数据为空");
            }
            if (StringUtils.isBlank(item.getElderName())) {
                throw exceptionCustomMsg(COMMON_ERROR, "第" + excelRow + "行【长者姓名】不能为空");
            }
            if (StringUtils.isBlank(item.getIdCard())) {
                throw exceptionCustomMsg(COMMON_ERROR, "第" + excelRow + "行【身份证号】不能为空");
            }
            if (item.getAmount() == null) {
                throw exceptionCustomMsg(COMMON_ERROR, "第" + excelRow + "行【消费券金额】不能为空");
            }
            if (StringUtils.isBlank(item.getBillingMonth())) {
                throw exceptionCustomMsg(COMMON_ERROR, "第" + excelRow + "行【账单归属月】不能为空");
            }
            validateBillingMonth(item.getBillingMonth());

            ElderlyInfoDO elderlyInfo = elderlyInfoMapper.selectOne(new LambdaQueryWrapperX<ElderlyInfoDO>()
                    .eq(ElderlyInfoDO::getElderName, item.getElderName())
                    .eq(ElderlyInfoDO::getIdCard, item.getIdCard())
                    .eqIfPresent(ElderlyInfoDO::getTenantId, currentTenantId));
            if (elderlyInfo == null) {
                throw exceptionCustomMsg(COMMON_ERROR, "第" + excelRow + "行未找到匹配长者（姓名+身份证）");
            }

            ExpenseOrderDO expenseOrder = expenseOrderMapper.selectOne(new LambdaQueryWrapperX<ExpenseOrderDO>()
                            .eqIfPresent(ExpenseOrderDO::getTenantId, elderlyInfo.getTenantId())
                            .eq(ExpenseOrderDO::getElderId, elderlyInfo.getId())
                            .eq(ExpenseOrderDO::getIsShow, true)
                            .eq(ExpenseOrderDO::getIsLock,0)
                            .eq(ExpenseOrderDO::getConfirmStatus,0)
                            .ne(ExpenseOrderDO::getPayStatus, 1)
                            .eq(ExpenseOrderDO::getBillingMonth, item.getBillingMonth())
                            .in(ExpenseOrderDO::getType, 1, 2));
            if (expenseOrder == null) {
                throw exceptionCustomMsg(COMMON_ERROR, "第" + excelRow + "行未找到可抵扣账单（需存在未缴费且可见的对应账单月账单）");
            }
            validateExpenseOrderNotConfirmedAndNotLock(expenseOrder);

            ElderlyConsumerVouchersDO data = ElderlyConsumerVouchersDO.builder()
                    .elderId(elderlyInfo.getId())
                    .billingMonth(item.getBillingMonth())
                    .amount(item.getAmount())
                    .remarks(item.getRemarks())
                    .status(0)
                    .returnStatus(0)
                    .tenantId(elderlyInfo.getTenantId())
                    .build();
            this.create(data);
        }
    }

    /**
     * 消费券录入对应未缴费的账单需要更新
     */
    private void updateExpenseOrder(ElderlyConsumerVouchersDO createReqVO) {
        ExpenseOrderDO expenseOrder = expenseOrderMapper.selectOne(new LambdaQueryWrapperX<ExpenseOrderDO>()
                .eq(ExpenseOrderDO::getElderId, createReqVO.getElderId())
                .eq(ExpenseOrderDO::getIsShow, true)
                .ne(ExpenseOrderDO::getPayStatus, 1)
                .eq(ExpenseOrderDO::getIsLock,0)
                .eq(ExpenseOrderDO::getConfirmStatus,0)
                .eq(ExpenseOrderDO::getBillingMonth, createReqVO.getBillingMonth())
                .in(ExpenseOrderDO::getType, 1, 2));

        if (expenseOrder == null) {
            return;
        }
        YearMonth yearMonth = YearMonth.parse(createReqVO.getBillingMonth(), DateTimeFormatter.ofPattern("yyyy-MM"));
        validateExpenseOrderNotConfirmedAndNotLock(expenseOrder);
        ExpenseOrderItemDO orderItem = new ExpenseOrderItemDO();
        orderItem.setExpenseOrderId(expenseOrder.getId());
        orderItem.setSourceExpenseItemId(createReqVO.getId());
        orderItem.setExpenseSource(BusinessConstants.CONSUMER_VOUCHER);
        orderItem.setCount(1);
        orderItem.setItemName("消费券抵扣");
        orderItem.setItemCategoryName("消费券");
        orderItem.setPrice(createReqVO.getAmount());
        orderItem.setActualPrice(createReqVO.getAmount());
        orderItem.setTotalAmount(createReqVO.getAmount());
        orderItem.setRoundAmount(orderItem.getTotalAmount().setScale(0, RoundingMode.HALF_UP));
        orderItem.setRoundTwoDecimalAmount(orderItem.getTotalAmount().setScale(2, RoundingMode.HALF_UP));
        orderItem.setCreatedTime(new Date());
        orderItem.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        orderItem.setStartDate(yearMonth.atDay(1));
        orderItem.setEndDate(yearMonth.atEndOfMonth());
        orderItem.setTenantId(createReqVO.getTenantId());
        orderItem.setPayStatus(0);
        orderItem.setDescription(StringUtils.isBlank(createReqVO.getRemarks()) ? yearMonth + "消费券抵扣" : createReqVO.getRemarks());
        expenseOrderItemMapper.insert(orderItem);

        BigDecimal currentAmount = expenseOrder.getActualAmount() != null ? expenseOrder.getActualAmount() : BigDecimal.ZERO;
        expenseOrder.setActualAmount(currentAmount.add(orderItem.getTotalAmount()));
        expenseOrderMapper.updateById(expenseOrder);

        createReqVO.setStatus(1);
        createReqVO.setOrderNumber(expenseOrder.getBillOrderNumber());
        createReqVO.setOrderId(expenseOrder.getId());
        elderlyConsumerVouchersMapper.updateById(createReqVO);
    }

    private void validateExpenseOrderNotConfirmedAndNotLock(ExpenseOrderDO expenseOrder) {
        if (Boolean.TRUE.equals(expenseOrder.getConfirmStatus())) {
            throw exception(EXPENSE_ORDER_ALREADY_CONFIRMED);
        }
        if (Boolean.TRUE.equals(expenseOrder.getIsLock())) {
            throw exception(EXPENSE_ORDER_ALREADY_LOCK);
        }
    }

    private void validateExists(Long id) {
        if (id == null || elderlyConsumerVouchersMapper.selectById(id) == null) {
            throw exception(COMMON_NOT_FOUND);
        }
    }

    /**
     * 校验账单月格式 yyyy-MM
     */
    private void validateBillingMonth(String billingMonth) {
        if (StringUtils.isBlank(billingMonth)) {
            return;
        }
        try {
            LocalDate.parse(billingMonth + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        } catch (DateTimeParseException ex) {
            throw exception(COMMON_ERROR, "账单归属月格式需为 yyyy-MM，例如 2025-01");
        }
    }

    /**
     * 更新退住结算单
     */
    private void updateRefundOrder(ElderlyConsumerVouchersDO vouchers){
        //创建长护险的时候要把费用生成对应的账单里面
        // 查询对应退住的账单，如果不存在则返回
        List<RefundSettlementOrderDO> refundSettlementOrderDOList = refundSettlementOrderMapper.selectList(new LambdaQueryWrapperX<RefundSettlementOrderDO>()
                .eq(RefundSettlementOrderDO::getElderId, vouchers.getElderId())
                .orderByDesc(RefundSettlementOrderDO::getCreatedTime)); // 月度账单
        if(CollectionUtil.isEmpty(refundSettlementOrderDOList)){
            return;
        }
        RefundSettlementOrderDO refundSettlementOrderDO = refundSettlementOrderDOList.get(0);

        // 创建账单费用项
        RefundSettlementOrderItemDO orderItem = new RefundSettlementOrderItemDO();
        orderItem.setRefundSettlementOrderId(refundSettlementOrderDO.getId());
        orderItem.setType(2);
        orderItem.setCount(1);
        orderItem.setItemName("消费券抵扣");
        orderItem.setRemarks(vouchers.getRemarks());
        orderItem.setPrice(vouchers.getAmount()); // 单价为负数表示抵扣
        orderItem.setRefundAmount(vouchers.getAmount()); // 实际单价
        orderItem.setActualRefundAmount(vouchers.getAmount()); // 总金额为负数表示抵扣
        orderItem.setTenantId(vouchers.getTenantId());
        orderItem.setSourceItemId(vouchers.getId());
        refundSettlementOrderItemMapper.insert(orderItem);
        // 更新账单总金额
        BigDecimal currentAmount = refundSettlementOrderDO.getRefundAmount() != null ? refundSettlementOrderDO.getRefundAmount() : BigDecimal.ZERO;
        BigDecimal newAmount = currentAmount.add(orderItem.getRefundAmount());
        refundSettlementOrderDO.setRefundAmount(newAmount);
        refundSettlementOrderMapper.updateById(refundSettlementOrderDO);
        //更新长护险状态
        vouchers.setStatus(1);
        elderlyConsumerVouchersMapper.updateById(vouchers);
    }

    /**
     * 删除退住账单对应的记录
     */
    private void deleteRefundOrderItem(Long vouchersId){
        RefundSettlementOrderItemDO refundSettlementOrderItemDO = refundSettlementOrderItemMapper.selectOne(new LambdaQueryWrapperX<RefundSettlementOrderItemDO>()
                .eq(RefundSettlementOrderItemDO::getSourceItemId, vouchersId));
        if(refundSettlementOrderItemDO == null){
            return;
        }
        RefundSettlementOrderDO refundSettlementOrderDO = refundSettlementOrderMapper.selectById(refundSettlementOrderItemDO.getRefundSettlementOrderId());
        refundSettlementOrderDO.setRefundAmount(refundSettlementOrderDO.getRefundAmount().subtract(refundSettlementOrderItemDO.getActualRefundAmount()));
        refundSettlementOrderMapper.updateById(refundSettlementOrderDO);
        refundSettlementOrderItemMapper.deleteById(refundSettlementOrderItemDO);
    }
}
