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

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.string.SerialCodeUtils;
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.BalancePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.enums.common.BooleanEnum;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

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

/**
 * 长者余额 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
public class BalanceServiceImpl implements BalanceService {

    @Resource
    private BalanceMapper balanceMapper;
    @Resource
    private BuildService buildService;

    @Resource
    private ElderlyInfoMapper elderlyInfoMapper;

    @Resource
    private BalanceRecordMapper balanceRecordMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private ExpenseMapper expenseMapper;

    @Resource
    private ExpenseItemMapper expenseItemMapper;


    @Override
    public PageResult<BalanceDO> getBalancePage(BalancePageReqVO pageReqVO) {
        Page<BalanceDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<BalanceDO> list = balanceMapper.getBalancePage(page, pageReqVO);
        for (BalanceDO balance : list) {
            balance.setBedName(buildService.getFullBedName(balance.getElderId()));
            if (balance.getAmount() == null) {
                balance.setAmount(BigDecimal.ZERO);
            }
        }
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    @LogRecord(type = ELDERLY_BALANCE_TYPE, subType = ELDERLY_BALANCE_CREATE_SUB_TYPE, bizNo = "{{#balance.id}}",
            success = ELDERLY_BALANCE_CREATE_SUCCESS)
    public void rechargeBalance(BalanceDO createReqVO) {
        BalanceDO balance = balanceMapper.selectOne(new LambdaQueryWrapperX<BalanceDO>().eq(BalanceDO::getElderId, createReqVO.getElderId()));
        List<BalanceRecordDO> balanceRecordList = createReqVO.getBalanceRecord();
        if (balance != null) {
            BigDecimal amount = balance.getAmount();
            for (BalanceRecordDO balanceRecord : balanceRecordList) {
                balanceRecord.setElderId(balance.getElderId());
                balanceRecord.setCreatedTime(new Date());
                balanceRecord.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());

                redisTemplate.opsForValue().increment(RedisKeyConstants.BALANCE_REDIS_KEY);
                Integer redisValue = (Integer) redisTemplate.opsForValue().get(RedisKeyConstants.BALANCE_REDIS_KEY);
                balanceRecord.setOrderNumber(SerialCodeUtils.generateOrderNumber(redisValue));
                balanceRecordMapper.insert(balanceRecord);
                amount = amount.add(balanceRecord.getChangeAmount());
            }
            balance.setAmount(amount);
            balanceMapper.updateById(balance);
        }
    }

    @Override
    public BalanceDO getBalance(Long elderId) {
        ElderlyInfoDO elderlyInfo = elderlyInfoMapper.selectById(elderId);
        BalanceDO balance = balanceMapper.selectOne(new LambdaQueryWrapperX<BalanceDO>().eq(BalanceDO::getElderId, elderId));
        balance.setElderName(elderlyInfo == null ? null : elderlyInfo.getElderName());
        balance.setBedName(buildService.getFullBedName(elderId));
        List<BalanceRecordDO> balanceRecords = balanceRecordMapper.selectList(new LambdaQueryWrapperX<BalanceRecordDO>().eq(BalanceRecordDO::getElderId, elderId));
        balance.setBalanceRecord(balanceRecords);
        return balance;
    }

    @Override
    public void createBalance(Long elderId) {
        BalanceDO balanceDO = balanceMapper.selectOne(new LambdaQueryWrapperX<BalanceDO>()
                .eq(BalanceDO::getElderId, elderId));
        if (balanceDO == null) {
            BalanceDO balance = new BalanceDO();
            balance.setElderId(elderId);
//            balance.setStatus(0);
            balance.setAmount(BigDecimal.ZERO);
            balance.setCreatedTime(new Date());
            balanceMapper.insert(balance);
        }
    }

    @Override
    @LogRecord(type = ELDERLY_BALANCE_TYPE, subType = ELDERLY_BALANCE_REFUND_SUB_TYPE, bizNo = "{{#balance.id}}",
            success = ELDERLY_BALANCE_REFUND_SUCCESS)
    public void refund(BalanceDO updateReqVO) {
        BalanceDO balance = balanceMapper.selectById(updateReqVO.getId());
        List<BalanceRecordDO> balanceRecord = updateReqVO.getBalanceRecord();
        if (balance != null) {
            BigDecimal amount = balance.getAmount();
            for (BalanceRecordDO balanceRecordDO : balanceRecord) {
                balanceRecordDO.setElderId(balance.getElderId());
                balanceRecordDO.setCreatedTime(new Date());
                balanceRecordDO.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
                balanceRecordMapper.insert(balanceRecordDO);
                amount = amount.add(balanceRecordDO.getChangeAmount());
            }
            balance.setAmount(amount);
            balanceMapper.updateById(balance);
        }
    }

    @Override
    public List<Map<String, BigDecimal>> test() {
        ElderlyInfoDO elderlyInfo = elderlyInfoMapper.selectById(12782l);
        ElderlyRetreatRecordDO elderlyRetreatRecord = new ElderlyRetreatRecordDO();
        elderlyRetreatRecord.setRetreatDate(LocalDate.parse("2024-11-20"));
        elderlyRetreatRecord.setElderlyId(12782l);
        Long startTenantId = 1l;
        ExpenseDO expense = expenseMapper.selectList(new LambdaQueryWrapperX<ExpenseDO>()
                        .eq(ExpenseDO::getElderId, elderlyInfo.getId())
                        .eq(ExpenseDO::getTenantId, startTenantId)
                        .orderByDesc(ExpenseDO::getCreatedTime))
                .stream()
                .findFirst()
                .get();
        List<ExpenseItemDO> expenseItems = expenseItemMapper.selectList(new LambdaQueryWrapperX<ExpenseItemDO>()
                .eq(ExpenseItemDO::getExpenseId, expense.getId())
                .eq(ExpenseItemDO::getIsMonthlyExpense, BooleanEnum.TRUE.getValue())
                .eq(ExpenseItemDO::getTenantId, startTenantId));
        List<Map<String, BigDecimal>> mapList = new ArrayList<>();
        for (ExpenseItemDO item : expenseItems) {

            // TODO 月费所有
            List<ExpenseItemDO> list = expenseItemMapper.selectCurrentMonthList(elderlyRetreatRecord.getRetreatDate(),
                    elderlyRetreatRecord.getElderlyId(), item.getType());
            LocalDate retreatDate = elderlyRetreatRecord.getRetreatDate();
            LocalDate firstDayOfMonth = retreatDate.withDayOfMonth(1);
            for (int i = 0; i < list.size(); i++) {
                Map<String, BigDecimal> map = new HashMap<>();
                ExpenseItemDO expenseItemDO = list.get(i);
                BigDecimal daysInCurrentMonth = BigDecimal.valueOf(elderlyRetreatRecord.getRetreatDate().lengthOfMonth());
                BigDecimal actualAmount = expenseItemDO.getActualAmount();
                LocalDate changeStartDate = expenseItemDO.getChangeStartDate();
                LocalDate changeEndDate = expenseItemDO.getChangeEndDate();

                if (null == changeEndDate && changeStartDate.isBefore(retreatDate)) {
                    changeEndDate = retreatDate;
                }

                // 变更产生的金额
                BigDecimal amount = BigDecimal.ZERO;
                // 变更整月差额
                BigDecimal changeAmount = BigDecimal.ZERO;
                if (null != changeStartDate && null != changeEndDate) {
                    long startDay = changeStartDate.toEpochDay();
                    long endDay = changeEndDate.toEpochDay();
                    BigDecimal currentDays = BigDecimal.valueOf(endDay - startDay + 1);
                    BigDecimal currentDayAmount = actualAmount.divide(daysInCurrentMonth, 8, RoundingMode.HALF_UP);
                    amount = currentDayAmount.multiply(currentDays);
                    System.out.println(i + ":amount = " + amount);
                    map.put("amount", amount);
                    if (i == list.size() - 1) {
                        mapList.add(map);
                        continue;
                    }
                    ExpenseItemDO nextItem = list.get(i + 1);
                    LocalDate lastDayOfMonth = firstDayOfMonth.with(TemporalAdjusters.lastDayOfMonth());
                    long epochDay = lastDayOfMonth.toEpochDay();
                    BigDecimal changeDay = BigDecimal.valueOf(epochDay - endDay);
                    BigDecimal changeDayAmount = nextItem.getActualAmount().divide(daysInCurrentMonth, 8, RoundingMode.HALF_UP);
                    BigDecimal currentAmount = currentDayAmount.multiply(changeDay);
                    changeAmount = changeDayAmount.multiply(changeDay);
                    BigDecimal finalAmount = currentAmount.subtract(changeAmount);
                    System.out.println(i + ":finalAmount = " + finalAmount);
                    map.put("finalAmount", finalAmount);


                }
                mapList.add(map);
            }
        }
        return mapList;
    }

    @Override
    public BalanceDO getBalanceAmountByElderId(Long elderId) {
        return balanceMapper.selectOne(new LambdaQueryWrapperX<BalanceDO>()
                .eq(BalanceDO::getElderId, elderId));
    }


}