package com.jeeplus.psimanage.dishManage.service;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jeeplus.common.TokenProvider;
import com.jeeplus.flowable.feign.IFlowableApi;
import com.jeeplus.psimanage.dishManage.domain.PsiDishLibrary;
import com.jeeplus.psimanage.dishManage.domain.PsiDishMenuDish;
import com.jeeplus.psimanage.dishManage.domain.PsiDishOrder;
import com.jeeplus.psimanage.dishManage.domain.PsiDishOrderDetail;
import com.jeeplus.psimanage.dishManage.domain.PsiDishRoom;
import com.jeeplus.psimanage.dishManage.domain.PsiDishType;
import com.jeeplus.psimanage.dishManage.mapper.PsiDishOrderDetailMapper;
import com.jeeplus.psimanage.dishManage.mapper.PsiDishOrderMapper;
import com.jeeplus.psimanage.dishManage.mapper.PsiDishRoomMapper;
import com.jeeplus.psimanage.dishManage.service.dto.PsiDishOrderExportDto;
import com.jeeplus.sys.feign.IRoleApi;
import com.jeeplus.sys.feign.IUserApi;
import com.jeeplus.sys.service.dto.RoleDTO;
import com.jeeplus.sys.service.dto.UserDTO;
import com.jeeplus.utils.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 点菜页面
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class PsiDishOrderService extends ServiceImpl<PsiDishOrderMapper, PsiDishOrder> {

    @Resource
    private PsiDishMenuService dishMenuService;

    @Resource
    private PsiDishOrderMapper dishOrderMapper;

    @Resource
    private PsiDishOrderDetailMapper dishOrderDetailMapper;

    @Resource
    private IFlowableApi flowTaskService;

    @Resource
    private PsiDishRoomMapper dishRoomMapper;

    private static final ExecutorService ASYNC_POOL = Executors.newFixedThreadPool(5);


    public List<PsiDishType> typeList() {
        return dishMenuService.orderTypeList();
    }

    public List<PsiDishLibrary> dishList(String typeId, String dishName) {
        return dishMenuService.orderDishList(typeId, dishName);
    }

    public IPage<PsiDishOrder> list(Page<PsiDishOrder> page, PsiDishOrder info) {
        QueryWrapper<PsiDishOrder> wrapper = new QueryWrapper<>();
        wrapper.eq("a.del_flag", "0");
        if (StringUtils.isNotBlank(info.getOrderNo())) {
            wrapper.like("a.order_no", info.getOrderNo());
        }
        if (StringUtils.isNotBlank(info.getRoomName())) {
            wrapper.like("a.room_name", info.getRoomName());
        }
        if (StringUtils.isNotBlank(info.getRoomNo())) {
            wrapper.like("a.room_no", info.getRoomNo());
        }
        if (StringUtils.isNotBlank(info.getOrderStatus())) {
            wrapper.eq("a.order_status", info.getOrderStatus());
        }
        if (StringUtils.isNotBlank(info.getSettleType())) {
            wrapper.eq("a.settle_type", info.getSettleType());
        }
        if (info.getCreateTimes() != null && info.getCreateTimes().length == 2) {
            wrapper.between("a.create_time", info.getCreateTimes()[0], info.getCreateTimes()[1]);
        }
        if (info.getSettleTimes() != null && info.getSettleTimes().length == 2) {
            wrapper.between("a.settle_time", info.getSettleTimes()[0], info.getSettleTimes()[1]);
        }
        return dishOrderMapper.findList(page, wrapper);
    }

    public List<PsiDishOrderExportDto> exportRecordList(Page<PsiDishOrder> page, PsiDishOrder info) {
        List<PsiDishOrder> orderList = this.list(page, info).getRecords();
        List<PsiDishOrderExportDto> result = new ArrayList<>();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (int i = 0; i < orderList.size(); i++) {
            PsiDishOrder order = orderList.get(i);
            PsiDishOrderExportDto exportDto = new PsiDishOrderExportDto();
            exportDto.setId(order.getId());
            exportDto.setRowNo(i + 1);
            exportDto.setOrderNo(order.getOrderNo());
            exportDto.setRoomNo(order.getRoomNo());
            exportDto.setRoomName(order.getRoomName());
            exportDto.setCreateTime(order.getCreateTime() == null ? "" : dateFormat.format(order.getCreateTime()));
            exportDto.setSettleTime(order.getSettleTime() == null ? "" : dateFormat.format(order.getSettleTime()));
            exportDto.setOrderStatus(orderStatusName(order.getOrderStatus()));
            exportDto.setSettleType(settleTypeName(order.getSettleType()));
            exportDto.setTotalAmount(formatMoney(order.getTotalAmount()));
            exportDto.setDiscountRate("1".equals(order.getSettleType()) ? formatMoney(order.getDiscountRate()) + "%" : "-");
            exportDto.setDiscountAmount(formatMoney(order.getDiscountAmount()));
            exportDto.setPayableAmount(formatMoney(order.getPayableAmount()));
            exportDto.setCreateName(order.getCreateName());
            result.add(exportDto);
        }
        return result;
    }

    private String orderStatusName(String value) {
        if ("0".equals(value)) {
            return "使用中";
        }
        if ("1".equals(value)) {
            return "已结账";
        }
        if ("2".equals(value)) {
            return "已取消";
        }
        return "-";
    }

    private String settleTypeName(String value) {
        if ("1".equals(value)) {
            return "折扣结账";
        }
        if ("2".equals(value)) {
            return "优惠结账";
        }
        if ("0".equals(value)) {
            return "结账";
        }
        return "-";
    }

    private String formatMoney(BigDecimal value) {
        return (value == null ? BigDecimal.ZERO : value).setScale(2, RoundingMode.HALF_UP).toPlainString();
    }

    public PsiDishOrder findOrderById(String id) {
        if (StringUtils.isBlank(id)) {
            return null;
        }
        PsiDishOrder order = dishOrderMapper.selectById(id);
        if (order == null || order.getDelFlag() == null || order.getDelFlag() != 0) {
            return null;
        }
        order.setDetailList(dishOrderDetailMapper.findSummaryByOrderId(order.getId()));
        order.setAddDishList(dishOrderDetailMapper.findAddDishList(order.getId()));
        order.setReduceDishList(dishOrderDetailMapper.findReduceDishList(order.getId()));
        return order;
    }

    public PsiDishOrder currentOrder(String roomId) {
        if (StringUtils.isBlank(roomId)) {
            return null;
        }
        PsiDishOrder order = dishOrderMapper.findCurrentByRoomId(roomId);
        if (order == null) {
            return null;
        }
        order.setDetailList(dishOrderDetailMapper.findSummaryByOrderId(order.getId()));
        order.setAddDishList(dishOrderDetailMapper.findAddDishList(order.getId()));
        order.setReduceDishList(dishOrderDetailMapper.findReduceDishList(order.getId()));
        return order;
    }

    public String submitOrder(PsiDishOrder info) {
        if (info == null || StringUtils.isBlank(info.getRoomId())) {
            return "请选择包房";
        }
        boolean hasValidDetail = info.getDetailList() != null && info.getDetailList().stream()
                .anyMatch(item -> StringUtils.isNotBlank(item.getDishId()) && item.getQuantity() != null && item.getQuantity() != 0);
        boolean hasSortDetail = info.getDetailList() != null && info.getDetailList().stream()
                .anyMatch(item -> StringUtils.isNotBlank(item.getDishId()) && item.getDishSort() != null);
        PsiDishRoom room = dishRoomMapper.queryById(info.getRoomId());
        if (room == null || "1".equals(room.getStatus())) {
            return "请选择启用状态的包房";
        }
        UserDTO userDto = SpringUtil.getBean(IUserApi.class).getByToken(TokenProvider.getCurrentToken());
        Date now = new Date();
        PsiDishOrder order = dishOrderMapper.findCurrentByRoomId(info.getRoomId());
        if (order == null && "1".equals(info.getSettleFlag()) && StringUtils.isNotBlank(info.getId())) {
            PsiDishOrder orderById = dishOrderMapper.selectById(info.getId());
            if (orderById != null && orderById.getDelFlag() != null && orderById.getDelFlag() == 0
                    && "0".equals(orderById.getOrderStatus()) && info.getRoomId().equals(orderById.getRoomId())) {
                order = orderById;
            }
        }
        if (!hasValidDetail) {
            if (order != null && hasSortDetail) {
                saveDishSort(order.getId(), info.getDetailList(), userDto, now);
            }
            if ("1".equals(info.getSettleFlag()) && order != null) {
                info.setRoomName(order.getRoomName());
                refreshOrderAmount(order.getId(), info, userDto, now);
                return "结账成功";
            }
            if (order != null && hasSortDetail) {
                return "排序保存成功";
            }
            return "请选择菜品";
        }
        boolean addDish = order != null;
        if (order == null) {
            order = createOrder(room, info, userDto, now);
            dishOrderMapper.insert(order);
            PsiDishRoom updateRoom = new PsiDishRoom();
            updateRoom.setId(room.getId());
            updateRoom.setUseStatus("1");
            updateRoom.setUpdateById(userDto.getId());
            updateRoom.setUpdateTime(now);
            dishRoomMapper.updateById(updateRoom);
        }
        Map<String, Integer> currentQuantityMap = getCurrentQuantityMap(order.getId());
        BigDecimal changeAmount = BigDecimal.ZERO;
        for (PsiDishOrderDetail item : info.getDetailList()) {
            if (StringUtils.isBlank(item.getDishId()) || item.getQuantity() == null || item.getQuantity() == 0) {
                continue;
            }
            int currentQuantity = currentQuantityMap.getOrDefault(item.getDishId(), 0);
            if (currentQuantity + item.getQuantity() < 0) {
                return item.getDishName() + "退菜数量不能大于已点数量";
            }
            PsiDishMenuDish dish = dishMenuService.findCurrentByDishId(item.getDishId());
            if (dish == null || !"0".equals(dish.getStatus())) {
                return "菜品不存在或已停用";
            }
            PsiDishOrderDetail detail = buildDetail(order, dish, item.getQuantity(), addDish ? "1" : "0", item.getDishSort(), userDto, now);
            detail.setReason(item.getReason());
            dishOrderDetailMapper.insert(detail);
            changeAmount = changeAmount.add(detail.getAmount());
            currentQuantityMap.put(item.getDishId(), currentQuantity + item.getQuantity());
        }
        if (hasSortDetail) {
            saveDishSort(order.getId(), info.getDetailList(), userDto, now);
        }
        info.setRoomName(order.getRoomName());
        refreshOrderAmount(order.getId(), info, userDto, now);
        return "1".equals(info.getSettleFlag()) ? "下单并结账成功" : (changeAmount.compareTo(BigDecimal.ZERO) < 0 ? "退菜成功" : "下单成功");
    }

    public String cancelOrder(String id) {
        if (StringUtils.isBlank(id)) {
            return "请选择要取消的订单";
        }
        PsiDishOrder order = dishOrderMapper.selectById(id);
        if (order == null || order.getDelFlag() == null || order.getDelFlag() != 0) {
            return "订单不存在";
        }
        if ("1".equals(order.getOrderStatus())) {
            return "已结账订单不能取消";
        }
        if ("2".equals(order.getOrderStatus())) {
            return "订单已取消";
        }
        if (!"0".equals(order.getOrderStatus())) {
            return "当前订单状态不能取消";
        }
        UserDTO userDto = SpringUtil.getBean(IUserApi.class).getByToken(TokenProvider.getCurrentToken());
        Date now = new Date();
        PsiDishOrder update = new PsiDishOrder();
        update.setId(order.getId());
        update.setOrderStatus("2");
        update.setUpdateById(userDto.getId());
        update.setUpdateTime(now);
        update.setSettleTime(now);
        dishOrderMapper.updateById(update);

        if (StringUtils.isNotBlank(order.getRoomId())) {
            PsiDishRoom room = new PsiDishRoom();
            room.setId(order.getRoomId());
            room.setUseStatus("0");
            room.setUpdateById(userDto.getId());
            room.setUpdateTime(now);
            dishRoomMapper.updateById(room);
        }
        return "取消成功";
    }

    private PsiDishOrder createOrder(PsiDishRoom room, PsiDishOrder info, UserDTO userDto, Date now) {
        PsiDishOrder order = new PsiDishOrder();
        order.setId(UUID.randomUUID().toString().replace("-", ""));
        order.setOrderNo("DC" + new SimpleDateFormat("yyyyMMddHHmmss").format(now));
        order.setRoomId(room.getId());
        order.setRoomNo(room.getRoomNo());
        order.setRoomName(room.getRoomName());
        order.setOrderStatus("0");
        order.setTotalAmount(BigDecimal.ZERO);
        order.setDiscountRate(new BigDecimal("100"));
        order.setDiscountAmount(BigDecimal.ZERO);
        order.setPayableAmount(BigDecimal.ZERO);
        order.setTasteRemark(info.getTasteRemark());
        order.setSpecialRequirements(info.getSpecialRequirements());
        order.setCreateById(userDto.getId());
        order.setCreateTime(now);
        order.setUpdateById(userDto.getId());
        order.setUpdateTime(now);
        order.setDelFlag(0);
        return order;
    }

    private PsiDishOrderDetail buildDetail(PsiDishOrder order, PsiDishMenuDish dish, Integer quantity, String isAddDish, Integer dishSort, UserDTO userDto, Date now) {
        BigDecimal salePrice = dish.getSalePrice() == null ? BigDecimal.ZERO : dish.getSalePrice();
        PsiDishOrderDetail detail = new PsiDishOrderDetail();
        detail.setId(UUID.randomUUID().toString().replace("-", ""));
        detail.setOrderId(order.getId());
        detail.setRoomId(order.getRoomId());
        detail.setDishId(dish.getDishId());
        detail.setDishCode(dish.getDishCode());
        detail.setDishName(dish.getDishName());
        detail.setTypeId(dish.getTypeId());
        detail.setTypeName(dish.getTypeName());
        detail.setUnit(dish.getUnit());
        detail.setSpec(dish.getSpec());
        detail.setSalePrice(salePrice);
        detail.setQuantity(quantity);
        detail.setAmount(salePrice.multiply(new BigDecimal(quantity)));
        detail.setDishSort(dishSort == null ? 0 : dishSort);
        detail.setIsAddDish(isAddDish);
        detail.setCreateById(userDto.getId());
        detail.setCreateTime(now);
        detail.setUpdateById(userDto.getId());
        detail.setUpdateTime(now);
        detail.setDelFlag(0);
        return detail;
    }


    private void saveDishSort(String orderId, List<PsiDishOrderDetail> detailList, UserDTO userDto, Date now) {
        if (StringUtils.isBlank(orderId) || detailList == null) {
            return;
        }
        for (PsiDishOrderDetail item : detailList) {
            if (StringUtils.isBlank(item.getDishId()) || item.getDishSort() == null) {
                continue;
            }
            dishOrderDetailMapper.updateDishSort(orderId, item.getDishId(), item.getDishSort(), userDto.getId(), now);
        }
    }

    private Map<String, Integer> getCurrentQuantityMap(String orderId) {
        Map<String, Integer> map = new HashMap<>();
        List<PsiDishOrderDetail> details = dishOrderDetailMapper.findSummaryByOrderId(orderId);
        details.forEach(item -> map.put(item.getDishId(), item.getQuantity() == null ? 0 : item.getQuantity()));
        return map;
    }

    private void refreshOrderAmount(String orderId, PsiDishOrder info, UserDTO userDto, Date now) {
        List<PsiDishOrderDetail> details = dishOrderDetailMapper.findSummaryByOrderId(orderId);
        BigDecimal totalAmount = BigDecimal.ZERO;
        for (PsiDishOrderDetail detail : details) {
            totalAmount = totalAmount.add(detail.getAmount() == null ? BigDecimal.ZERO : detail.getAmount());
        }
        PsiDishOrder update = new PsiDishOrder();
        update.setId(orderId);
        update.setTotalAmount(totalAmount);
        update.setUpdateById(userDto.getId());
        update.setUpdateTime(now);
        if ("1".equals(info.getSettleFlag())) {
            fillSettleAmount(update, totalAmount, info);
            update.setOrderStatus("1");
            update.setSettleTime(now);
            PsiDishOrder order = dishOrderMapper.selectById(orderId);
            if (order != null) {
                PsiDishRoom room = new PsiDishRoom();
                room.setId(order.getRoomId());
                room.setUseStatus("0");
                room.setUpdateById(userDto.getId());
                room.setUpdateTime(now);
                dishRoomMapper.updateById(room);
            }
        }
        if("1".equals(update.getSettleType()) || "2".equals(update.getSettleType())){
            ASYNC_POOL.execute(() -> {
                //-----------------------发起流程--------------------
                //String settleType = "1".equals(update.getSettleType()) ? "折扣结账" : "优惠结账";
                ////发起折扣结账至老板待办
                //Map map = SpringUtil.getBean(IFlowableApi.class).getByNameForFen("景聚庭-折扣结账");
                //String procDefId = map.get("id")+"";
                //String procDefKey = map.get("key")+"";
                //String businessTable = "psi_dish_order";
                //String businessId = update.getId();
                //String nowDateFormat = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                //String title =userDto.getName() +" 在 "+nowDateFormat+" 发起了 ["+info.getRoomName()+"] "+settleType;
                //RoleDTO roleDTO = SpringUtil.getBean(IRoleApi.class).getRoleDTOByName2("老板");
                //List<UserDTO> notifiedPartyUsers =SpringUtil.getBean(IUserApi.class).findListByRoleId(roleDTO.getId());
                //UserDTO assigneeDTO = notifiedPartyUsers.get(0);
                //String assignee = assigneeDTO.getId();
                //String recordType = "";
                //Map<String, String> newMap = pingMap(procDefId, procDefKey, businessTable, businessId, title,assignee, recordType,userDto.getId());
                //Map<String ,Map<String,String >>  allMap = new HashMap<>();
                //allMap.put("折扣结账",newMap);
                //SpringUtil.getBean(IFlowableApi.class).startForFenNew(allMap);
                //-----------------------发送通知--------------------
                String settleType = "1".equals(update.getSettleType()) ? "折扣结账" : "优惠结账";
                String nowDateFormat = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String day = format.format(new Date());
                RoleDTO roleDTO = SpringUtil.getBean(IRoleApi.class).getRoleDTOByName2("老板");
                List<UserDTO> notifiedPartyUsers =SpringUtil.getBean(IUserApi.class).findListByRoleId(roleDTO.getId());
                UserDTO assigneeDTO = notifiedPartyUsers.get(0);
                String title =userDto.getName() +" 在 "+nowDateFormat+" 发起了 ["+info.getRoomName()+"] "+settleType;
                //获取目前领用流程的taskid，根据procInsId去查taskId
                String uuid = "";
                if (null != assigneeDTO) {
                    uuid = UUID.randomUUID().toString();
                    //发送通知
                    Map<String ,String > map = new HashMap<>();
                    map.put("taskId",uuid);
                    map.put("title",title);
                    map.put("defId",update.getId());
                    map.put("taskName","景聚庭-优惠结账");
                    map.put("createUser","管理员");
                    map.put("createTime",day);
                    map.put("noticeName",assigneeDTO.getLoginName());
                    map.put("noticeId",assigneeDTO.getId());
                    map.put("createById",userDto.getId());
                    flowTaskService.add(map);
                }
            });
        }
        dishOrderMapper.updateById(update);
    }

    public Map<String, String> pingMap(String procDefId, String procDefKey, String businessTable,
                                       String businessId, String title, String assignee, String recordType,String userId) {
        Map<String, String> map = new HashMap();
        map.put("procDefId", procDefId);
        map.put("procDefKey", procDefKey);
        map.put("businessTable", businessTable);
        map.put("businessId", businessId);
        map.put("title", title);
        map.put("assignee", assignee);
        map.put("recordType", recordType);
        map.put("userId", userId);
        return map;
    }

    private void fillSettleAmount(PsiDishOrder update, BigDecimal totalAmount, PsiDishOrder info) {
        String settleType = StringUtils.isBlank(info.getSettleType()) ? "0" : info.getSettleType();
        BigDecimal discountRate = info.getDiscountRate() == null ? new BigDecimal("100") : info.getDiscountRate();
        BigDecimal discountAmount = info.getDiscountAmount() == null ? BigDecimal.ZERO : info.getDiscountAmount();
        BigDecimal payableAmount = totalAmount;
        if ("1".equals(settleType)) {
            if (discountRate.compareTo(BigDecimal.ZERO) < 0) {
                discountRate = BigDecimal.ZERO;
            }
            if (discountRate.compareTo(new BigDecimal("100")) > 0) {
                discountRate = new BigDecimal("100");
            }
            payableAmount = totalAmount.multiply(discountRate).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
            discountAmount = totalAmount.subtract(payableAmount);
        } else if ("2".equals(settleType)) {
            if (discountAmount.compareTo(BigDecimal.ZERO) < 0) {
                discountAmount = BigDecimal.ZERO;
            }
            if (discountAmount.compareTo(totalAmount) > 0) {
                discountAmount = totalAmount;
            }
            payableAmount = totalAmount.subtract(discountAmount);
            discountRate = new BigDecimal("100");
        } else {
            settleType = "0";
            discountRate = new BigDecimal("100");
            discountAmount = BigDecimal.ZERO;
        }
        update.setSettleType(settleType);
        update.setDiscountRate(discountRate);
        update.setDiscountAmount(discountAmount);
        update.setPayableAmount(payableAmount);
    }
}
