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.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.sys.vo.ElderOverheadChargeRespVO;
import cn.iocoder.yudao.module.system.controller.admin.sys.vo.OverheadChargeImportExcelVO;
import cn.iocoder.yudao.module.system.controller.admin.sys.vo.SysOverheadChargePageReqVO;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.sys.vo.OverheadChargeSheetImportExcelVO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.OverheadChargeDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.SysChargeCategoryDO;
import cn.iocoder.yudao.module.system.dal.mysql.biz.SysChargeCategoryMapper;
import cn.iocoder.yudao.module.system.dal.mysql.biz.SysOverheadChargeMapper;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;


@Service
@Validated
@Slf4j
public class SysOverheadChargeServiceImpl implements SysOverheadChargeService {

    private String in_sequence = "inSequence";
    private String daily = "daily";
    private String month = "month";


    @Autowired
    private SysOverheadChargeMapper sysOverheadChargeMapper;
    @Autowired
    private SysChargeCategoryMapper sysChargeCategoryMapper;
    @Resource
    private TenantMapper tenantMapper;

    @Override
    public void add(OverheadChargeDO overheadChargeDO) {
        int hour = 24;
        overheadChargeDO.setCreateDate(LocalDateTime.now());
        //计算小时费用，按照24小时
        if (overheadChargeDO.getChargeType().equals(daily)) {
            double price = overheadChargeDO.getPrice();
            double hourlyPrice = BigDecimal.valueOf(price / hour).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();
            overheadChargeDO.setHourlyPrice(hourlyPrice);
            overheadChargeDO.setDayExpense(price);
        } else if (overheadChargeDO.getChargeType().equals(month)) {
            //按月
            double price = overheadChargeDO.getPrice();
            int num = 30 * hour;
            double hourlyPrice = BigDecimal.valueOf(price / num).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();
            double dayPrice = BigDecimal.valueOf(hourlyPrice * hour).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();

            overheadChargeDO.setHourlyPrice(hourlyPrice);
            overheadChargeDO.setDayExpense(dayPrice);

        } else if (overheadChargeDO.getChargeType().equals(in_sequence)) {
            //按次数收费
            double price = overheadChargeDO.getPrice();
            overheadChargeDO.setDayExpense(price);
        }
        sysOverheadChargeMapper.insert(overheadChargeDO);
    }

    @Override
    public OverheadChargeDO getInfoById(Long id) {
        OverheadChargeDO overheadCharge = sysOverheadChargeMapper.selectById(id);
        if (overheadCharge != null) {
            SysChargeCategoryDO sysChargeCategory = sysChargeCategoryMapper.selectById(overheadCharge.getSuperiorsId());
            overheadCharge.setCategoryName(sysChargeCategory == null ? null : sysChargeCategory.getName());
        }
        return overheadCharge;
    }

    @Override
    public PageResult<OverheadChargeDO> queryPage(SysOverheadChargePageReqVO pageVO) {
        Page<OverheadChargeDO> page = new Page<>(pageVO.getPageNo(), pageVO.getPageSize());
        List<OverheadChargeDO> list = sysOverheadChargeMapper.queryPage(page, pageVO);
        for (OverheadChargeDO overheadChargeDO : list) {
            overheadChargeDO.setTenantName(tenantMapper.selectById(overheadChargeDO.getTenantId()).getName());
        }
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public void edit(OverheadChargeDO overheadChargeDO) {
        int hour = 24;
        if (overheadChargeDO.getChargeType().equals(daily)) {
            double price = overheadChargeDO.getPrice();
            double hourlyPrice = BigDecimal.valueOf(price / hour).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();
            overheadChargeDO.setHourlyPrice(hourlyPrice);
            overheadChargeDO.setDayExpense(price);
        } else if (overheadChargeDO.getChargeType().equals(month)) {
            //按月
            double price = overheadChargeDO.getPrice();
            int num = 30 * hour;
            double hourlyPrice = BigDecimal.valueOf(price / num).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();
            double dayPrice = BigDecimal.valueOf(hourlyPrice * hour).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();

            overheadChargeDO.setHourlyPrice(hourlyPrice);
            overheadChargeDO.setDayExpense(dayPrice);
        } else if (overheadChargeDO.getChargeType().equals(in_sequence)) {
            //按次数收费
            double price = overheadChargeDO.getPrice();
            overheadChargeDO.setDayExpense(price);
        }
        sysOverheadChargeMapper.updateById(overheadChargeDO);
    }

    @Override
    public void delete(Long id) {
        sysOverheadChargeMapper.deleteById(id);
    }

    @Override
    public void move(OverheadChargeDO overheadChargeDO) {
        List<Long> ids = overheadChargeDO.getMoveIds();
        if (CollectionUtil.isNotEmpty(ids)) {
            for (Long id : ids) {
                OverheadChargeDO dbOverheadCharge = sysOverheadChargeMapper.selectById(id);
                if (dbOverheadCharge != null) {
                    dbOverheadCharge.setSuperiorsId(overheadChargeDO.getSuperiorsId());
                    sysOverheadChargeMapper.updateById(dbOverheadCharge);
                }
            }
        }
    }

    @Override
    @TenantIgnore
    public List<OverheadChargeDO> getListByType(String type, Long startTenantId) {
        if (startTenantId == null){
            startTenantId = SecurityFrameworkUtils.getLoginUser().getTenantId();
        }
        List<Long> categoryIdList = new ArrayList<>();
        List<SysChargeCategoryDO> categoryList = sysChargeCategoryMapper.selectList(new LambdaQueryWrapperX<SysChargeCategoryDO>()
                .eq(SysChargeCategoryDO::getIdentification, type)
                .eq(SysChargeCategoryDO::getStatus,1)
                .eqIfPresent(SysChargeCategoryDO::getTenantId, startTenantId));
        for (SysChargeCategoryDO sysChargeCategory : categoryList) {
            //查询节点下面的所有子节点
            List<Long> subIds = new ArrayList<>();
            subIds.add(sysChargeCategory.getId());
            getSubcategories(sysChargeCategory.getId(), subIds);
            categoryIdList.addAll(subIds);
        }

        Long finalStartTenantId = startTenantId;
        List<List<OverheadChargeDO>> collect = categoryIdList.stream().map(item -> sysOverheadChargeMapper
                        .selectList(new LambdaQueryWrapperX<OverheadChargeDO>()
                                .eq(OverheadChargeDO::getSuperiorsId, item)
                                .eq(OverheadChargeDO::getStatus,1)
                                .eqIfPresent(OverheadChargeDO::getTenantId, finalStartTenantId)))
                .collect(Collectors.toList());
        return collect.stream().flatMap(List::stream).collect(Collectors.toList());
    }

    @Override
    public List<ElderOverheadChargeRespVO> getListByElderlyId(Long elderlyId) {
        List<ElderOverheadChargeRespVO> list = sysOverheadChargeMapper.getListByElderlyId(elderlyId);
        return list;
    }

    @Override
    public List<OverheadChargeDO> getListByChargeCategoryId(Long chargeCategoryId) {
        List<OverheadChargeDO> list = sysOverheadChargeMapper.getListByChargeCategoryId(chargeCategoryId);
        return list;
    }

    public void getSubcategories(Long parentId, List<Long> subIds) {
        // 递归查询 parentId 下的所有子分类（可能有多个），避免只取一个子节点导致收费项目不完整
        List<SysChargeCategoryDO> children = sysChargeCategoryMapper
                .selectList(new LambdaQueryWrapperX<SysChargeCategoryDO>()
                        .eq(SysChargeCategoryDO::getParentId, parentId));
        if (CollectionUtil.isNotEmpty(children)) {
            for (SysChargeCategoryDO child : children) {
                // 防止脏数据/循环引用导致无限递归
                if (child == null || child.getId() == null || Objects.equals(child.getId(), parentId)) {
                    continue;
                }
                // 防止重复添加（例如上层已经加过）
                if (!subIds.contains(child.getId())) {
                    subIds.add(child.getId());
                }
                getSubcategories(child.getId(), subIds);
            }
        }
    }


    @Override
    @Transactional
    public Map<String,Object> importOverheadCharge(List<OverheadChargeImportExcelVO> dataList) {
        Map<String,Object> map = new HashMap<>();
        List<OverheadChargeImportExcelVO> errorList = new ArrayList<>();
        int hour = 24;
        int i = 0;
        for (OverheadChargeImportExcelVO overheadChargeImportExcelVO : dataList) {
            OverheadChargeDO overheadCharge = new OverheadChargeDO();
            overheadCharge.setChargeName(overheadChargeImportExcelVO.getChargeName());
            overheadCharge.setCreateDate(LocalDateTime.now());
            overheadCharge.setTenantId(TenantContextHolder.getTenantId());
            overheadCharge.setPrice(overheadChargeImportExcelVO.getPrice());

            Optional<SysChargeCategoryDO> optional = sysChargeCategoryMapper.selectList(new LambdaQueryWrapperX<SysChargeCategoryDO>()
                            .eq(SysChargeCategoryDO::getName, overheadChargeImportExcelVO.getCategoryName()))
                    .stream().findFirst();
            if (!optional.isPresent()){
                overheadChargeImportExcelVO.setErrorMsg(overheadChargeImportExcelVO.getCategoryName() + "分类不存在");
                errorList.add(overheadChargeImportExcelVO);
                continue;
            }
            SysChargeCategoryDO sysChargeCategory = optional.get();
            overheadCharge.setSuperiorsId(String.valueOf(sysChargeCategory.getId()));
            overheadCharge.setCreateBy(SecurityFrameworkUtils.getLoginUserNickname());
            double price = overheadChargeImportExcelVO.getPrice();
            if (overheadChargeImportExcelVO.getChargeType().equals("/月")){
                int num = 30 * hour;
                double hourlyPrice = BigDecimal.valueOf(price / num).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();
                double dayPrice = BigDecimal.valueOf(hourlyPrice * hour).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();

                overheadCharge.setHourlyPrice(hourlyPrice);
                overheadCharge.setDayExpense(dayPrice);
                overheadCharge.setChargeType(month);

            }else if (overheadChargeImportExcelVO.getChargeType().equals("/日")){
                double hourlyPrice = BigDecimal.valueOf(price / hour).setScale(8, BigDecimal.ROUND_DOWN).doubleValue();
                overheadCharge.setHourlyPrice(hourlyPrice);
                overheadCharge.setDayExpense(price);
                overheadCharge.setChargeType(daily);
            }else {
                overheadCharge.setDayExpense(price);
                overheadCharge.setChargeType(in_sequence);
            }
            sysOverheadChargeMapper.insert(overheadCharge);
            i++;
        }
        map.put("successCount",i);
        map.put("failCount",errorList.size());
        map.put("errorList",errorList);
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    public Map<String, Object> importOverheadChargeBySheet(MultipartFile file, Boolean updateSupport) {
        // 1. 读取 Excel 数据
        Map<String, List<OverheadChargeSheetImportExcelVO>> sheetDataMap = new LinkedHashMap<>();
        EasyExcel.read(file.getInputStream(), OverheadChargeSheetImportExcelVO.class, new ReadListener<OverheadChargeSheetImportExcelVO>() {
            private List<OverheadChargeSheetImportExcelVO> list = new ArrayList<>();

            @Override
            public void invoke(OverheadChargeSheetImportExcelVO data, AnalysisContext context) {
                data.setSheetName(context.readSheetHolder().getSheetName());
                list.add(data);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                sheetDataMap.put(context.readSheetHolder().getSheetName(), new ArrayList<>(list));
                list.clear();
            }
        }).doReadAll();

        // 2. 数据处理
        List<OverheadChargeSheetImportExcelVO> errorList = new ArrayList<>();
        AtomicInteger successCount = new AtomicInteger(0);

        if (CollUtil.isEmpty(sheetDataMap)) {
            return Collections.emptyMap();
        }

        // 3. 获取所有相关的费用分类
        List<String> sheetNames = new ArrayList<>(sheetDataMap.keySet());
        Map<String, SysChargeCategoryDO> categoryMap = sysChargeCategoryMapper.selectList(
                new LambdaQueryWrapper<SysChargeCategoryDO>()
                        .in(SysChargeCategoryDO::getName, sheetNames)
                        .eq(SysChargeCategoryDO::getTenantId, TenantContextHolder.getTenantId())
        ).stream().collect(Collectors.toMap(SysChargeCategoryDO::getName, Function.identity()));

        List<OverheadChargeDO> toCreateList = new ArrayList<>();
        List<OverheadChargeDO> toUpdateList = new ArrayList<>();

        // 4. 遍历每个 Sheet 进行处理
        sheetDataMap.forEach((sheetName, dataList) -> {
            SysChargeCategoryDO category = categoryMap.get(sheetName);
            if (category == null) {
                dataList.forEach(item -> {
                    item.setErrorMsg("费用分类 '" + sheetName + "' 不存在");
                    errorList.add(item);
                });
                return; // continue to next sheet
            }

            // 获取该分类下已有的收费项
            Map<String, OverheadChargeDO> existingChargeMap = sysOverheadChargeMapper.selectList(
                    new LambdaQueryWrapper<OverheadChargeDO>()
                            .eq(OverheadChargeDO::getSuperiorsId, category.getId())
                            .eq(OverheadChargeDO::getTenantId, TenantContextHolder.getTenantId())
            ).stream().collect(Collectors.toMap(OverheadChargeDO::getChargeName, Function.identity()));

            dataList.forEach(item -> {
                // 数据校验
                if (StrUtil.isBlank(item.getChargeName())) {
                    item.setErrorMsg("服务项目名称不能为空");
                    errorList.add(item);
                    return;
                }
                BigDecimal price = parsePrice(item.getPrice());
                if (price == null || price.compareTo(BigDecimal.ZERO) < 0) {
                    item.setErrorMsg("价格格式不正确");
                    errorList.add(item);
                    return;
                }

                OverheadChargeDO existingCharge = existingChargeMap.get(item.getChargeName());
                if (existingCharge != null) {
                    if (Boolean.TRUE.equals(updateSupport)) {
                        // 更新逻辑
                        OverheadChargeDO updateDO = BeanUtils.toBean(existingCharge, OverheadChargeDO.class);
                        updateDO.setPrice(price.doubleValue());
                        updateDO.setRemarks(item.getServiceContent());
                        setChargeTypeAndPrice(updateDO, item.getUnitOrRemark(), price.doubleValue());
                        toUpdateList.add(updateDO);
                        successCount.incrementAndGet();
                    } else {
                        item.setErrorMsg("服务项目 '" + item.getChargeName() + "' 已存在");
                        errorList.add(item);
                    }
                } else {
                    // 新增逻辑
                    OverheadChargeDO createDO = new OverheadChargeDO();
                    createDO.setChargeName(item.getChargeName());
                    createDO.setPrice(price.doubleValue());
                    createDO.setRemarks(item.getServiceContent());
                    createDO.setSuperiorsId(String.valueOf(category.getId()));
                    createDO.setTenantId(TenantContextHolder.getTenantId());
                    createDO.setCreateBy(SecurityFrameworkUtils.getLoginUserNickname());
                    createDO.setCreateDate(LocalDateTime.now());
                    setChargeTypeAndPrice(createDO, item.getUnitOrRemark(), price.doubleValue());
                    toCreateList.add(createDO);
                    successCount.incrementAndGet();
                }
            });
        });

        // 5. 数据库操作
        if (CollUtil.isNotEmpty(toCreateList)) {
            sysOverheadChargeMapper.insertBatch(toCreateList);
        }
        if (CollUtil.isNotEmpty(toUpdateList)) {
            sysOverheadChargeMapper.updateBatch(toUpdateList);
        }

        // 6. 返回结果
        Map<String, Object> map = new HashMap<>();
        map.put("successCount", successCount.get());
        map.put("failCount", errorList.size());
        map.put("errorList", errorList);
        return map;
    }

    private void setChargeTypeAndPrice(OverheadChargeDO chargeDO, String unit, double price) {
        int hour = 24;
        if ("/月".equals(unit)) {
            chargeDO.setChargeType(month);
            int num = 30 * hour;
            double hourlyPrice = BigDecimal.valueOf(price / num).setScale(8, RoundingMode.DOWN).doubleValue();
            double dayPrice = BigDecimal.valueOf(hourlyPrice * hour).setScale(8, RoundingMode.DOWN).doubleValue();
            chargeDO.setHourlyPrice(hourlyPrice);
            chargeDO.setDayExpense(dayPrice);
        } else if ("/日".equals(unit)) {
            chargeDO.setChargeType(daily);
            double hourlyPrice = BigDecimal.valueOf(price / hour).setScale(8, RoundingMode.DOWN).doubleValue();
            chargeDO.setHourlyPrice(hourlyPrice);
            chargeDO.setDayExpense(price);
        } else { // 次、件、张、小时等都视为按次
            chargeDO.setChargeType(in_sequence);
            chargeDO.setDayExpense(price);
            chargeDO.setHourlyPrice(0);
        }
    }

    private BigDecimal parsePrice(String raw) {
        if (StrUtil.isBlank(raw)) {
            return null;
        }
        String s = raw.trim();
        // 兼容：20元、 1元/2元、 1/2、 20 等
        s = s.replace("元", "");
        if (s.contains("/")) {
            s = s.split("/")[0];
        }
        s = s.trim();
        if (StrUtil.isBlank(s)) {
            return null;
        }
        try {
            return new BigDecimal(s);
        } catch (Exception e) {
            return null;
        }
    }

}
