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

import cn.hutool.core.collection.CollectionUtil;
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.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.nursing.vo.NursingPlanPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.nursing.vo.NursingPlanItemRespVO;
import cn.iocoder.yudao.module.system.controller.admin.nursing.vo.NursingPlanLogRespVO;
import cn.iocoder.yudao.module.system.controller.admin.nursing.vo.NursingPlanLowFrequencyByFloorRespVO;
import cn.iocoder.yudao.module.system.controller.admin.nursing.vo.NursingPlanRespVO;
import cn.iocoder.yudao.module.system.controller.admin.nursing.vo.NursingPlanSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.nursing.vo.NursingPlanItemWithElderVO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.DailyExpensesDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyContractDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyInfoDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.NurseItemDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.NursingCategoryDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.NursingPlanDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.NursingPlanItemDO;
import cn.iocoder.yudao.module.system.dal.mysql.biz.DailyExpensesMapper;
import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyContractMapper;
import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyInfoMapper;
import cn.iocoder.yudao.module.system.dal.mysql.biz.NurseItemMapper;
import cn.iocoder.yudao.module.system.dal.mysql.biz.NursingCategoryMapper;
import cn.iocoder.yudao.module.system.dal.mysql.biz.NursingPlanItemMapper;
import cn.iocoder.yudao.module.system.dal.mysql.biz.NursingPlanMapper;
import cn.iocoder.yudao.module.system.dal.mysql.elderly.ElderlyNursingLogMapper;
import cn.iocoder.yudao.module.system.enums.common.BooleanEnum;
import cn.iocoder.yudao.module.system.service.biz.BuildService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ELDERLY_NURSE_PLAN_EXISTS;


/**
 * 护理计划 Service 实现类
 *
 * @author 系统管理员
 */
@Service
@Validated
public class NursingPlanServiceImpl implements NursingPlanService {

    @Resource
    private NursingPlanMapper nursingPlanMapper;
    @Resource
    private NursingPlanItemMapper nursingPlanItemMapper;
    @Resource
    private ElderlyContractMapper elderlyContractMapper;
    @Resource
    private BuildService buildService;
    @Resource
    private ElderlyInfoMapper elderlyInfoMapper;
    @Resource
    private NurseItemMapper nurseItemMapper;
    @Resource
    private NursingCategoryMapper nursingCategoryMapper;
    @Resource
    private DailyExpensesMapper dailyExpensesMapper;

    @Autowired
    private ElderlyNursingLogMapper elderlyNursingLogMapper;

    @Override
    @Transactional
    public void createNursingPlan(NursingPlanSaveReqVO createReqVO) {
        NursingPlanDO dbNursingPlanDO = nursingPlanMapper.selectOne(new LambdaQueryWrapperX<NursingPlanDO>()
                .eq(NursingPlanDO::getElderId, createReqVO.getElderId())
                .eq(NursingPlanDO::getType, createReqVO.getType()));

        if (dbNursingPlanDO != null) {
            throw exception(ELDERLY_NURSE_PLAN_EXISTS);
        }

        ElderlyContractDO elderlyContract = elderlyContractMapper.selectOne(new LambdaQueryWrapperX<ElderlyContractDO>()
                .eq(ElderlyContractDO::getElderId, createReqVO.getElderId()));

        NursingPlanDO nursingPlan = new NursingPlanDO();
        BeanUtils.copyProperties(createReqVO, nursingPlan);
        nursingPlan.setCreatedTime(new Date());
        nursingPlan.setStatus(1);
        nursingPlan.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());


        if (elderlyContract != null && elderlyContract.getExpireTime() != null) {
            nursingPlan.setExpirationDate(elderlyContract
                    .getExpireTime()
                    .toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate());
        } else {
            nursingPlan.setExpirationDate(nursingPlan.getEffectiveDate().plusYears(99));
        }
        nursingPlanMapper.insert(nursingPlan);

        List<NursingPlanItemDO> items = createReqVO.getItems();
        for (NursingPlanItemDO item : items) {
            item.setId(null);
            item.setNursingPlanId(nursingPlan.getId());
            item.setCreatedTime(new Date());
            item.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
            nursingPlanItemMapper.insert(item);
            //创建日常费用
//            createdDailyExpense(createReqVO.getElderId(), item);
        }

    }

    private void createdDailyExpense(Long elderId, NursingPlanItemDO item) {
        NurseItemDO itemDO = nurseItemMapper.selectById(item.getNurseItemId());
        if (itemDO != null && itemDO.getType().equals(BooleanEnum.TRUE.getValue())){
            DailyExpensesDO dailyExpenses = new DailyExpensesDO();
            dailyExpenses.setType(4); // 4：额外
            dailyExpenses.setElderId(elderId);
            dailyExpenses.setAttributionBillTime(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
            dailyExpenses.setItemId(itemDO.getId());
            NursingCategoryDO nursingCategoryDO = nursingCategoryMapper.selectById(itemDO.getCategoryId());
            dailyExpenses.setItemCategory(nursingCategoryDO == null ? null : nursingCategoryDO.getCategoryName());
            dailyExpenses.setItemName(itemDO.getItemName());
            dailyExpenses.setPrice(itemDO.getPrice());
            dailyExpenses.setAmount(itemDO.getPrice());
            dailyExpenses.setRoundAmount(dailyExpenses.getAmount().setScale(0, RoundingMode.HALF_UP));
            dailyExpenses.setRemarks("长者增加护理项目[" + itemDO.getItemName() + "]的费用");
            dailyExpenses.setCount(1);
            dailyExpenses.setCreatedTime(new Date());
            dailyExpenses.setAccountingTime(new Date());
            dailyExpenses.setIsGenerateBill(BooleanEnum.FALSE.getValue());
            dailyExpensesMapper.insert(dailyExpenses);
        }
    }

    @Override
    public NursingPlanRespVO getNursingPlan(Long id) {
        NursingPlanRespVO respVO = new NursingPlanRespVO();

        NursingPlanDO nursingPlanDO = nursingPlanMapper.selectById(id);
        BeanUtils.copyProperties(nursingPlanDO, respVO);
        respVO.setBedName(buildService.getFullBedName(nursingPlanDO.getElderId()));
        ElderlyInfoDO elderlyInfoDO = elderlyInfoMapper.selectById(nursingPlanDO.getElderId());
        respVO.setNurseLevelName(elderlyInfoDO == null ? null : elderlyInfoDO.getNurseLevelName());

        List<NursingPlanItemRespVO> allItems =
                nursingPlanItemMapper.selectNursingPlanItemsWithFrequencyCategory(nursingPlanDO.getId(), null,null);
        Map<Boolean, List<NursingPlanItemRespVO>> itemGroup = allItems.stream()
                .collect(Collectors.partitioningBy(item -> BooleanEnum.TRUE.getValue().equals(item.getIsExtra())));

        respVO.setItems(itemGroup.getOrDefault(false, Collections.emptyList()));
        respVO.setExtraItems(itemGroup.getOrDefault(true, Collections.emptyList()));
        return respVO;
    }

    @Override
    public void updateNursingPlan(NursingPlanSaveReqVO updateReqVO) {
        NursingPlanDO nursingPlanDO = nursingPlanMapper.selectById(updateReqVO.getId());
        BeanUtils.copyProperties(updateReqVO, nursingPlanDO);
        nursingPlanMapper.updateById(nursingPlanDO);

        List<NursingPlanItemDO> items = updateReqVO.getItems();
        //查询原先的子项
        List<NursingPlanItemDO> dbItems = nursingPlanItemMapper.selectList(new LambdaQueryWrapperX<NursingPlanItemDO>()
                .eq(NursingPlanItemDO::getNursingPlanId, updateReqVO.getId())
                .eq(NursingPlanItemDO::getIsDelete, BooleanEnum.FALSE.getValue()));

        List<NursingPlanItemDO> removeList = dbItems
                .stream()
                .filter(item -> !items.contains(item))
                .collect(Collectors.toList());
        //逻辑删除
        for (NursingPlanItemDO nursingPlanItemDO : removeList) {
            nursingPlanItemDO.setIsDelete(BooleanEnum.TRUE.getValue());
            nursingPlanItemMapper.updateById(nursingPlanItemDO);
        }

        //新增
        List<NursingPlanItemDO> addList = items
                .stream()
                .filter(item -> item.getId() == null)
                .collect(Collectors.toList());

        for (NursingPlanItemDO nursingPlanItemDO : addList) {
            nursingPlanItemDO.setNursingPlanId(updateReqVO.getId());
            nursingPlanItemDO.setCreatedTime(new Date());
            nursingPlanItemDO.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
            nursingPlanItemMapper.insert(nursingPlanItemDO);
        }

        //编辑
        List<NursingPlanItemDO> updateList = items.stream().filter(item -> item.getId() != null).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(updateList)) {
            for (NursingPlanItemDO nursingPlanItemDO : updateList) {
                nursingPlanItemMapper.updateById(nursingPlanItemDO);
            }
        }

    }

    @Override
    public PageResult<NursingPlanRespVO> getNursingPlanPage(NursingPlanPageReqVO pageReqVO) {
        Page<NursingPlanRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<NursingPlanRespVO> list = nursingPlanMapper.getNursingPlanPage(page, pageReqVO);
        for (NursingPlanRespVO respVO : list) {
            String fullBedName = buildService.getFullBedName(respVO.getElderId());
            respVO.setBedName(fullBedName);
        }
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public void copyNursingPlan(NursingPlanSaveReqVO saveReqVO) {
        NursingPlanDO dbNursingPlanDO = nursingPlanMapper.selectOne(new LambdaQueryWrapperX<NursingPlanDO>()
                .eq(NursingPlanDO::getElderId, saveReqVO.getElderId())
                .eq(NursingPlanDO::getType, saveReqVO.getType()));


        List<NursingPlanItemDO> items = saveReqVO.getItems();
        List<NursingPlanItemDO> extraItems = saveReqVO.getExtraItems();

        if (dbNursingPlanDO == null) {
            dbNursingPlanDO = new NursingPlanDO();

            ElderlyContractDO elderlyContract = elderlyContractMapper
                    .selectOne(new LambdaQueryWrapperX<ElderlyContractDO>()
                            .eq(ElderlyContractDO::getElderId, saveReqVO.getElderId()));

            BeanUtils.copyProperties(saveReqVO, dbNursingPlanDO);
            dbNursingPlanDO.setCreatedTime(new Date());
            dbNursingPlanDO.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());

            if (elderlyContract != null) {
                dbNursingPlanDO.setExpirationDate(elderlyContract
                        .getExpireTime()
                        .toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDate());
            } else {
                dbNursingPlanDO.setExpirationDate(LocalDate.MAX);
            }
            nursingPlanMapper.insert(dbNursingPlanDO);


            //生成配置项
            for (NursingPlanItemDO item : items) {
                item.setId(null);
                item.setNursingPlanId(dbNursingPlanDO.getId());
                item.setCreatedTime(new Date());
                item.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
                nursingPlanItemMapper.insert(item);
                createdDailyExpense(saveReqVO.getElderId(), item);
            }
            for (NursingPlanItemDO extraItem : extraItems) {
                extraItem.setId(null);
                extraItem.setIsExtra(BooleanEnum.TRUE.getValue());
                extraItem.setNursingPlanId(dbNursingPlanDO.getId());
                extraItem.setCreatedTime(new Date());
                extraItem.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
                nursingPlanItemMapper.insert(extraItem);
                createdDailyExpense(saveReqVO.getElderId(), extraItem);
            }
        } else {
            if (dbNursingPlanDO.getElderId().equals(saveReqVO.getElderId())) {
                throw exception(ELDERLY_NURSE_PLAN_EXISTS);
            }
        }
    }

    @Override
    public List<NursingPlanLogRespVO> getNursingPlanByElderId(Long elderId) {
        return elderlyNursingLogMapper.selectPlanLogByElderIdWithItemName(elderId);
    }

    @Override
    public List<NursingPlanLowFrequencyByFloorRespVO> getLowFrequencyPlanItemsByFloorId(Long floorId,Long tenantId) {
        List<ElderlyInfoDO> elders = elderlyInfoMapper.selectList(new LambdaQueryWrapperX<ElderlyInfoDO>()
                .select(ElderlyInfoDO::getId, ElderlyInfoDO::getElderName)
                .eq(ElderlyInfoDO::getFloorId, floorId)
                .eq(ElderlyInfoDO::getInStatus, 1));
        if (CollectionUtil.isEmpty(elders)) {
            return Collections.emptyList();
        }

        List<Long> elderIds = elders.stream().map(ElderlyInfoDO::getId).collect(Collectors.toList());
        List<NursingPlanItemWithElderVO> rows = nursingPlanItemMapper
                .selectPlanItemsWithElderByElderIdsAndFrequencyCategoryType(elderIds, 2, LocalDate.now(), tenantId);

        Map<Long, List<NursingPlanItemRespVO>> itemsByElderId = rows.stream()
                .filter(r -> r.getElderId() != null)
                .collect(Collectors.groupingBy(NursingPlanItemWithElderVO::getElderId,
                        Collectors.mapping(r -> {
                            NursingPlanItemRespVO item = new NursingPlanItemRespVO();
                            BeanUtils.copyProperties(r, item);
                            return item;
                        }, Collectors.toList())));

        List<NursingPlanLowFrequencyByFloorRespVO> result = new ArrayList<>(elders.size());
        for (ElderlyInfoDO elder : elders) {
            NursingPlanLowFrequencyByFloorRespVO vo = new NursingPlanLowFrequencyByFloorRespVO();
            vo.setElderId(elder.getId());
            vo.setElderName(elder.getElderName());
            vo.setItems(itemsByElderId.getOrDefault(elder.getId(), Collections.emptyList()));
            result.add(vo);
        }
        return result.stream()
                .filter(e->CollectionUtil.isNotEmpty(e.getItems()))
                .collect(Collectors.toList());
    }

    @Override
    public List<NursingPlanDO> selectAll() {
        return nursingPlanMapper.selectList();
    }
}
