package com.sw.mes.store.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sw.mes.core.exception.BusinessException;
import com.sw.mes.core.response.ResponsePage;
import com.sw.mes.core.response.Result;
import com.sw.mes.core.response.ResultGenerator;
import com.sw.mes.core.utils.DynmaicQueryWrapper;
import com.sw.mes.core.utils.ExcelUtil;
import com.sw.mes.core.utils.RequestPage;
import com.sw.mes.store.dto.*;
import com.sw.mes.store.entity.WarehouseInventory;
import com.sw.mes.store.entity.WarehouseOutputBill;
import com.sw.mes.store.entity.WarehouseOutputBillDetail;
import com.sw.mes.store.entity.WarehouseOutputRecordBillDetail;
import com.sw.mes.store.global.StoreGlobalSwitch;
import com.sw.mes.store.global.StoreNamespace;
import com.sw.mes.store.service.WarehouseInventoryService;
import com.sw.mes.store.service.WarehouseOutputBillDetailService;
import com.sw.mes.store.service.WarehouseOutputBillService;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author wupeng
 * @Description 出库单
 * @Date 18:44 2023/7/31
 * @Param
 * @return
 **/
@RestController
@RequestMapping("/warehouse/outputBill")
@Slf4j
@Api(tags = "线边仓-出库单")
public class WarehouseOutputBillController {
    @Autowired
    ModelMapper modelMapper;

    @Autowired
    private WarehouseOutputBillService warehouseOutputBillService;

    @Autowired
    private WarehouseOutputBillDetailService detailService;

    @Autowired
    private WarehouseInventoryService warehouseInventoryService;

    @ApiOperation(value = "分页查询出库单列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "rows", value = "行数", dataType = "integer", paramType = "query"),
            @ApiImplicitParam(name = "page", value = "页码", dataType = "integer", paramType = "query"),
    })
    @GetMapping("/listByPage")
    public Result<ResponsePage<WarehouseOutputBillDto>> listByPage(ServletRequest request, @ApiIgnore RequestPage requestPage) {
        QueryWrapper<WarehouseOutputBill> queryWrapper = DynmaicQueryWrapper.bySearchFilter(request, WarehouseOutputBill.class);
        Page<WarehouseOutputBill> page = new Page<>(requestPage.getPage(), requestPage.getRows());
        String orderDirection = requestPage.getOrderDirection();
        String orderField = requestPage.getOrderField();
        page.addOrder(RequestPage.ORDER_DIRECTION_DESC.equals(orderDirection) ? OrderItem.desc(orderField) : OrderItem.asc(orderField));
        Page<WarehouseOutputBill> listPage = warehouseOutputBillService.page(page, queryWrapper);
        List<WarehouseOutputBillDto> dtos = listPage.getRecords().stream()
                .map(entity -> toLazyDto(entity))
                .collect(Collectors.toList());
        ResponsePage<WarehouseOutputBillDto> responsePage = new ResponsePage(dtos, listPage.getTotal());
        return ResultGenerator.success(responsePage);
    }

    @ApiOperation(value = "出库单详细信息")
    @GetMapping("/{id}")
    public Result<WarehouseOutputBillDto> get(@PathVariable @ApiParam(value = "出库单id") Long id) {
        WarehouseOutputBill warehouseOutputBill = warehouseOutputBillService.getById(id);
        if (null == warehouseOutputBill) {
            return ResultGenerator.fail("该出库单不存在");
        }
        List<WarehouseOutputBillDetail> outputBillDetails = detailService.getByBillId(id);
        //fixme 查一下库存接口，更新一下正确的库存 这里这么做是因为上面接口查出来的数据会包含已删除的数据
        try {
            if (StoreGlobalSwitch.enable(StoreNamespace.NAMESPACE_COMMON_SWITCH, "openQueryDetailQuantity", false)) {
                queryQuantityByOutputBillDetail(outputBillDetails);
            }
        } catch (BusinessException ex) {
            warehouseOutputBill.setOutputBillDetails(outputBillDetails);
            return ResultGenerator.fail(ex.getMessage());
        }
        warehouseOutputBill.setOutputBillDetails(outputBillDetails);
        return ResultGenerator.success(toEagerDto(warehouseOutputBill));
    }

    /**
     * 根据detail信息更新一把对应的信息
     *
     * @param outputBillDetails
     */
    private void queryQuantityByOutputBillDetail(List<WarehouseOutputBillDetail> outputBillDetails) throws BusinessException {
        if (CollectionUtils.isEmpty(outputBillDetails)) {
            return;
        }
        //组装查询数据
        List<WarehouseInventoryDto> inventoryDtos = new ArrayList<>();
        for (WarehouseOutputBillDetail detail : outputBillDetails) {
            if(null != detail.getWarehouseId() && null  != detail.getMaterialModelId()){
                WarehouseInventoryDto warehouseInventoryDto = new WarehouseInventoryDto();
                warehouseInventoryDto.setBatchNo(detail.getBatchNo());
                warehouseInventoryDto.setWarehouseId(detail.getWarehouseId());
                warehouseInventoryDto.setMaterialModelId(detail.getMaterialModelId());
                warehouseInventoryDto.setMaterialModelCode(detail.getMaterialModelCode());
                warehouseInventoryDto.setUniqueCode(detail.getUniqueCode());
                inventoryDtos.add(warehouseInventoryDto);
            }
        }
        //查出来真是的库存数据
        Map<String, BigDecimal> inventoryMap = new HashMap<>();
        try {
            inventoryMap = warehouseInventoryService.getQuantity(inventoryDtos);
        } catch (BusinessException ex) {
            log.error("business exception in getQuantity from queryQuantityByOutputBillDetail", ex);
            throw ex;
        } catch (Exception e) {//如果数据出错，则直接返回，按照原来的给
            log.error("error in getQuantity from queryQuantityByOutputBillDetail", e);
            return;
        }
        //更新库存
        for (WarehouseOutputBillDetail detail : outputBillDetails) {
            String batchNo = detail.getBatchNo();
            String uniqueCode = detail.getUniqueCode();
            Long materialModelId = detail.getMaterialModelId();
            Long warehouseId = detail.getWarehouseId();
            if (StringUtils.isNotBlank(uniqueCode)) {
                if (inventoryMap.containsKey(uniqueCode)) {
                    detail.setQuantity(inventoryMap.get(uniqueCode));
                } else {//如果库存查不出来，就是已经为0
                    detail.setQuantity(new BigDecimal(0));
                }
            } else {
                String key = StringUtils.isBlank(batchNo) ? materialModelId + "-" + warehouseId + "" : batchNo + "-" + materialModelId + "-" + warehouseId;
                if (inventoryMap.containsKey(key)) {
                    detail.setQuantity(inventoryMap.get(key));
                } else {//如果库存查不出来，就是已经为0
                    detail.setQuantity(new BigDecimal(0));
                }
            }
        }
    }

    @ApiOperation(value = "人工创建出库单")
    @PostMapping("/create")
    public Result<String> create(@RequestBody @Valid @ApiParam(value = "出库单对象") WarehouseOutputBillDto warehouseOutputBillDto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultGenerator.fail(bindingResult.getFieldError().getDefaultMessage());
        }
        try {
            WarehouseOutputBill warehouseOutputBill = fromDto(warehouseOutputBillDto);
            //保存入库单和入库明细
            warehouseOutputBillService.saveOutputBill(warehouseOutputBill);
            return ResultGenerator.success();
        } catch (Exception e) {
            return ResultGenerator.fail(e.getMessage());
        }
    }

    @ApiOperation(value = "确认出库")
    @PostMapping("/deduction")
    public Result<String> output(@RequestBody @Valid @ApiParam(value = "出库单对象") WarehouseOutputBillDto warehouseOutputBillDto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultGenerator.fail(bindingResult.getFieldError().getDefaultMessage());
        }
        try {
            WarehouseOutputBill warehouseOutputBill = fromDto(warehouseOutputBillDto);
            //审核通过，扣减库存
            warehouseOutputBillService.auditDeductionAtomic(warehouseOutputBill);
            return ResultGenerator.success();
        } catch (Exception e) {
            return ResultGenerator.fail(e.getMessage());
        }
    }
    @ApiOperation(value = "WMS发起入库")
    @PostMapping("/wmsInitiate")
    public Result wmsInitiate(@RequestBody @Valid @ApiParam(value = "出库单对象") WarehouseOutputBillDto warehouseOutputBillDto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultGenerator.fail(bindingResult.getFieldError().getDefaultMessage());
        }
        try {
            String type = warehouseOutputBillDto.getType();
            String billNo = warehouseOutputBillDto.getBillNo();
            List<WarehouseOutputBillDetail> outputBillDetails = detailService.getByBillNo(billNo);
            Result result = warehouseOutputBillService.wmsInitiate(outputBillDetails, billNo, type);
            if(result.getCode().equals(200)){
                WarehouseOutputBill warehouseOutputBillByCode = warehouseOutputBillService.findWarehouseOutputBillByCode(billNo);
                warehouseOutputBillByCode.setStatus("待入库");
                warehouseOutputBillService.saveOrUpdate(warehouseOutputBillByCode);
                warehouseOutputBillByCode.getOutputBillDetails().forEach(entity->{
                    entity.setStatus(warehouseOutputBillByCode.getStatus());
                });
                detailService.saveOrUpdateBatch(warehouseOutputBillByCode.getOutputBillDetails());
                return ResultGenerator.success();
            }else {
                return ResultGenerator.fail("发起WMS入库失败,失败原因:"+result.getMessage());
            }
        } catch (Exception e) {
           throw new BusinessException("发起WMS入库失败,失败原因:"+ e);
        }
    }
    @ApiOperation(value = "取消出库单")
    @PostMapping("/cancel")
    public Result<String> cancel(@RequestBody @Valid @ApiParam(value = "出库单对象") WarehouseOutputBillDto warehouseOutputBillDto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultGenerator.fail(bindingResult.getFieldError().getDefaultMessage());
        }
        try {
            WarehouseOutputBill warehouseOutputBill = fromDto(warehouseOutputBillDto);
            //取消出库单
            warehouseOutputBillService.cancelApply(warehouseOutputBill.getBillNo());
            return ResultGenerator.success();
        } catch (Exception e) {
            return ResultGenerator.fail(e.getMessage());
        }
    }

    @ApiOperation(value = "退料出库接收WMS状态反馈信息 ")
    @PostMapping("/statusReception")
    public Result statusReception(@RequestBody @Valid @ApiParam(value = "接收对象") WmsOutputBillDto dto) {
        try {
            warehouseOutputBillService.statusReception(dto);
            return ResultGenerator.success();
        } catch (Exception e) {
            return ResultGenerator.fail(e.getMessage());
        }
    }

    @ApiOperation(value = "拒绝出库")
    @PostMapping("/rejection")
    public Result<String> rejectApply(@RequestBody @Valid @ApiParam(value = "出库单对象") WarehouseOutputBillDto warehouseOutputBillDto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultGenerator.fail(bindingResult.getFieldError().getDefaultMessage());
        }
        try {
            WarehouseOutputBill warehouseOutputBill = fromDto(warehouseOutputBillDto);
            //拒绝出库
            warehouseOutputBillService.rejectApply(warehouseOutputBill);
            return ResultGenerator.success();
        } catch (Exception e) {
            return ResultGenerator.fail(e.getMessage());
        }
    }

    /**
     * 场景：直接出库，无需审核
     **/
    @ApiOperation(value = "直接出库")
    @PostMapping("/direct")
    public Result direct(@RequestBody @Valid @ApiParam(value = "出库单对象") WarehouseOutputBillDto warehouseOutputBillDto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ResultGenerator.fail(bindingResult.getFieldError().getDefaultMessage());
        }
        try {
            WarehouseOutputBill warehouseOutputBill = fromDto(warehouseOutputBillDto);
            warehouseOutputBill.setAutoAudit(true);
            warehouseOutputBillService.saveOutputBill(warehouseOutputBill);
            return ResultGenerator.success();
        } catch (Exception e) {
            return ResultGenerator.fail(e.getMessage());
        }
    }
    @ApiOperation(value = "导出出库单明细接口")
    @GetMapping("/exportExcel")
    public void export(ServletRequest request, HttpServletResponse response) {
        try {
            LambdaQueryWrapper<WarehouseOutputBillDetail> queryWrapper = new LambdaQueryWrapper<>();
            String searchLikeBillNo = request.getParameter("search_LIKE_billNo");
            if(StringUtils.isNotBlank(searchLikeBillNo)){
                queryWrapper.like(WarehouseOutputBillDetail::getOutputBillNo,searchLikeBillNo);
            }
            String searchEqStatus = request.getParameter("search_EQ_status");
            if(StringUtils.isNotBlank(searchEqStatus)){
                queryWrapper.eq(WarehouseOutputBillDetail::getStatus,searchEqStatus);
            }
            String searchGteCreateTime = request.getParameter("search_GTE_createTime");
            if(StringUtils.isNotBlank(searchGteCreateTime)){
                queryWrapper.ge(WarehouseOutputBillDetail::getCreateTime,searchGteCreateTime);
            }
            String searchLtCreateTime = request.getParameter("search_LT_createTime");
            if(StringUtils.isNotBlank(searchLtCreateTime)){
                queryWrapper.le(WarehouseOutputBillDetail::getCreateTime,searchLtCreateTime);
            }
            String searchLikeType = request.getParameter("search_LIKE_type");
            if(StringUtils.isNotBlank(searchLikeType)){
                queryWrapper.like(WarehouseOutputBillDetail::getType,searchLikeType);
            }
            String searchGteUpdateTime = request.getParameter("search_GTE_updateTime");
            if(StringUtils.isNotBlank(searchGteUpdateTime)){
                queryWrapper.ge(WarehouseOutputBillDetail::getUpdateTime,searchGteUpdateTime);
            }
            String searchLtUpdateTime = request.getParameter("search_LT_updateTime");
            if(StringUtils.isNotBlank(searchLtUpdateTime)){
                queryWrapper.le(WarehouseOutputBillDetail::getUpdateTime,searchLtUpdateTime);
            }
            String searchLikeDestination = request.getParameter("search_LIKE_destination");
            if(StringUtils.isNotBlank(searchLikeDestination)){
                queryWrapper.like(WarehouseOutputBillDetail::getDestination,searchLikeDestination);
            }
            List<WarehouseOutputBillDetail> entities = detailService.list(queryWrapper);
            List<WarehouseOutputBillDetailDto> dtos = entities.stream()
                    .map(storeOutputBill -> toLazyDetailsDto(storeOutputBill))
                    .collect(Collectors.toList());
            ExcelUtil.exportExcel(dtos, "出库单明细", WarehouseOutputBillDetailDto.class, response);
        } catch (Exception e) {
            throw new RuntimeException("出库单明细导出失败，错误信息：" + e.getMessage());
        }
    }

    public WarehouseOutputBillDto toLazyDto(WarehouseOutputBill entity) {
        return modelMapper.map(entity, WarehouseOutputBillDto.class);
    }

    public WarehouseOutputBillDto toEagerDto(WarehouseOutputBill entity) {
        WarehouseOutputBillDto dto = modelMapper.map(entity, WarehouseOutputBillDto.class);
        return dto;
    }

    public WarehouseOutputBill fromDto(WarehouseOutputBillDto dto) {
        return modelMapper.map(dto, WarehouseOutputBill.class);
    }
    public WarehouseOutputBillDetailDto toLazyDetailsDto(WarehouseOutputBillDetail entity) {
        return modelMapper.map(entity, WarehouseOutputBillDetailDto.class);
    }

}