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

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.excel.DepositExcelVO;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.DepositRecordPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;


/**
 * 长者押金 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
public class DepositServiceImpl implements DepositService {

    @Resource
    private DepositMapper depositMapper;
    @Resource
    private DepositRecordMapper depositRecordMapper;

    @Autowired
    private BuildService buildService;

    @Autowired
    private ElderlyInfoMapper elderlyInfoMapper;

    @Resource
    private ExpenseItemMapper expenseItemMapper;

    @Resource
    private TenantService tenantService;
    @Resource
    private RefundSettlementOrderMapper refundSettlementOrderMapper;
    @Resource
    private RefundSettlementOrderItemMapper refundSettlementOrderItemMapper;
    @Override
    @LogRecord(type = ELDERLY_DEPOSIT_TYPE, subType = ELDERLY_DEPOSIT_CREATE_SUB_TYPE, bizNo = "{{#dbDeposit.id}}",
            success = ELDERLY_DEPOSIT_CREATE_SUCCESS)
    public void pay(DepositDO createReqVO) {
        DepositDO dbDeposit = depositMapper.selectById(createReqVO.getId());
        BigDecimal amount = dbDeposit.getAmount();

        List<DepositRecordDO> records = createReqVO.getRecords();
        for (DepositRecordDO record : records) {
            amount = amount.add(record.getAmount());
            record.setType(1);
            record.setCreatedTime(new Date());
            record.setOrderNo("Y" + RandomUtil.randomNumbers(15));
            record.setElderId(dbDeposit.getElderId());
            record.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
            depositRecordMapper.insert(record);
        }
        dbDeposit.setAmount(amount);
        // 更新退住结算单
        updateRefundOrder(createReqVO);
        depositMapper.updateById(dbDeposit);
    }

    @Override
    public DepositDO getDepositDetails(Long id) {
        DepositDO dbDeposit = depositMapper.selectById(id);

        ElderlyInfoDO elderlyInfoDO = elderlyInfoMapper.selectById(dbDeposit.getElderId());
        dbDeposit.setElderName(elderlyInfoDO == null ? null : elderlyInfoDO.getElderName());
        String fullBedName = buildService.getFullBedName(dbDeposit.getElderId());
        dbDeposit.setBedName(fullBedName);

        //查询所有明细
        List<DepositRecordDO> records = depositRecordMapper.selectList(new LambdaQueryWrapperX<DepositRecordDO>().eq(DepositRecordDO::getElderId, dbDeposit.getElderId()));
        dbDeposit.setRecords(records);

        return dbDeposit;
    }

    @Override
    @LogRecord(type = ELDERLY_DEPOSIT_TYPE, subType = ELDERLY_DEPOSIT_REFUND_SUB_TYPE, bizNo = "{{#dbDeposit.id}}",
            success = ELDERLY_DEPOSIT_REFUND_SUCCESS)
    public void refund(DepositDO updateReqVO) {
        DepositDO dbDeposit = depositMapper.selectById(updateReqVO.getId());
        BigDecimal amount = dbDeposit.getAmount();
        if (dbDeposit != null) {
            List<DepositRecordDO> records = updateReqVO.getRecords();
            for (DepositRecordDO record : records) {
                //类型，1：缴纳  2：消费 3：退款
                record.setType(3);
                record.setElderId(dbDeposit.getElderId());
                record.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
                record.setCreatedTime(new Date());
                record.setOrderNo("Y" + RandomUtil.randomNumbers(15));
                depositRecordMapper.insert(record);

                amount = amount.subtract(record.getAmount());
            }
            dbDeposit.setAmount(amount);
            depositMapper.updateById(dbDeposit);
        }
    }

    @Override
    public PageResult<DepositDO> getDepositPage(DepositRecordPageReqVO pageReqVO) {
        Page<DepositDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<DepositDO> list = depositMapper.getDepositPage(page, pageReqVO);
        for (DepositDO depositDO : list) {
            String fullBedName = buildService.getFullBedName(depositDO.getElderId());
            depositDO.setBedName(fullBedName);

            TenantDO tenant = tenantService.getTenant(depositDO.getTenantId());
            depositDO.setTenantName(tenant == null ? null : tenant.getName());
        }
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    @LogRecord(type = ELDERLY_DEPOSIT_TYPE, subType = ELDERLY_DEPOSIT_CONSUMPTION_SUB_TYPE, bizNo = "{{#dbDeposit.id}}",
            success = ELDERLY_DEPOSIT_CONSUMPTION_SUCCESS)
    public void consumption(DepositDO updateReqVO) {
        DepositDO dbDeposit = depositMapper.selectById(updateReqVO.getId());
        BigDecimal amount = dbDeposit.getAmount();
        if (dbDeposit != null) {
            List<DepositRecordDO> records = updateReqVO.getRecords();
            for (DepositRecordDO record : records) {
                //类型，1：缴纳  2：消费 3：退款
                record.setType(2);
                record.setElderId(dbDeposit.getElderId());
                record.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
                record.setCreatedTime(new Date());
                record.setOrderNo("Y" + RandomUtil.randomNumbers(15));
                depositRecordMapper.insert(record);

                amount = amount.subtract(record.getAmount());
            }
            dbDeposit.setAmount(amount);
            depositMapper.updateById(dbDeposit);
        }
    }

    @Override
    @LogRecord(type = ELDERLY_DEPOSIT_TYPE, subType = ELDERLY_DEPOSIT_EXPORT_SUB_TYPE, bizNo = "1",
            success = ELDERLY_DEPOSIT_EXPORT_SUCCESS)
    public List<DepositExcelVO> exportDepositExcel(DepositRecordPageReqVO pageReqVO) {
        Page<DepositDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<DepositDO> list = depositMapper.getDepositPage(page, pageReqVO);
        return list.stream().map(item -> {
            ElderlyInfoDO elderlyInfo = elderlyInfoMapper.selectById(item.getElderId());

            DepositExcelVO excelVO = new DepositExcelVO();
            excelVO.setElderName(elderlyInfo.getElderName());
            excelVO.setBedName(buildService.getFullBedName(elderlyInfo.getId()));
            excelVO.setAmount(item.getAmount());
            if (elderlyInfo.getInStatus() == 0) {
                excelVO.setInStatusStr("未入住");
            } else if (elderlyInfo.getInStatus() == 1) {
                excelVO.setInStatusStr("已入住");
            } else if (elderlyInfo.getInStatus() == 2) {
                excelVO.setInStatusStr("已退住");
            }
            Optional<DepositRecordDO> optional = depositRecordMapper.selectList(new LambdaQueryWrapperX<DepositRecordDO>()
                    .eq(DepositRecordDO::getElderId, elderlyInfo.getId())
                    .orderByDesc(DepositRecordDO::getCreatedTime)).stream().findFirst();

            if (optional.isPresent()) {
                DepositRecordDO depositRecord = optional.get();
                ExpenseItemDO expenseItemDO = expenseItemMapper.selectById(depositRecord.getItemId());
                excelVO.setDepositName(expenseItemDO.getItemName());
            } else {
                excelVO.setDepositName("医药备用金");
            }
            return excelVO;
        }).collect(Collectors.toList());
    }

    private void updateRefundOrder(DepositDO depositDO){
        // 查询对应退住的账单，如果不存在则返回
        List<RefundSettlementOrderDO> refundSettlementOrderDOList = refundSettlementOrderMapper.selectList(new LambdaQueryWrapperX<RefundSettlementOrderDO>()
                .eq(RefundSettlementOrderDO::getElderId, depositDO.getElderId())
                .orderByDesc(RefundSettlementOrderDO::getCreatedTime)); // 月度账单
        if(CollectionUtil.isEmpty(refundSettlementOrderDOList)){
            return;
        }
        RefundSettlementOrderDO refundSettlementOrderDO = refundSettlementOrderDOList.get(0);
        List<RefundSettlementOrderItemDO> refundSettlementOrderItemDOList = new ArrayList<>();
        for (DepositRecordDO record : depositDO.getRecords()) {
            // 创建账单费用项
            RefundSettlementOrderItemDO orderItem = new RefundSettlementOrderItemDO();
            orderItem.setRefundSettlementOrderId(refundSettlementOrderDO.getId());
            orderItem.setType(4);
            orderItem.setCount(1);
            orderItem.setItemName(record.getItemName());
            orderItem.setRemarks(record.getRemarks());
            orderItem.setPrice(record.getAmount()); // 单价
            orderItem.setRefundAmount(record.getAmount().negate()); // 金额
            orderItem.setActualRefundAmount(record.getAmount().negate()); // 总金额
            orderItem.setTenantId(record.getTenantId());
            orderItem.setSourceItemId(record.getId());
            refundSettlementOrderItemDOList.add(orderItem);
            // 更新账单总金额
            BigDecimal currentAmount = refundSettlementOrderDO.getRefundAmount() != null ? refundSettlementOrderDO.getRefundAmount() : BigDecimal.ZERO;
            BigDecimal newAmount = currentAmount.add(orderItem.getRefundAmount());
            refundSettlementOrderDO.setRefundAmount(newAmount);
        }
        refundSettlementOrderItemMapper.insertBatch(refundSettlementOrderItemDOList);
        refundSettlementOrderMapper.updateById(refundSettlementOrderDO);
    }
}