package com.neusoft.smart.pos.inventory.service;

import com.neusoft.smart.pos.dto.inventory.request.PostInventoryStoreBatchRequest;
import com.neusoft.smart.pos.dto.inventory.request.PutInventoryStoreBatchRequest;
import com.neusoft.smart.pos.framework.exception.BusinessCommonException;
import com.neusoft.smart.pos.framework.utils.ExcelUtils;
import com.neusoft.smart.pos.inventory.constants.InventoryConstant;
import com.neusoft.smart.pos.inventory.constants.InventoryErrorCode;
import com.neusoft.smart.pos.dto.inventory.request.InventoryCheckStockDetailRequest;
import com.neusoft.smart.pos.dto.inventory.request.InventoryCheckStockListRequest;
import com.neusoft.smart.pos.dto.inventory.response.InventoryCheckStockDetailResponse;
import com.neusoft.smart.pos.dto.inventory.response.InventoryCheckStockListResponse;
import com.neusoft.smart.pos.dto.inventory.response.InventoryCheckStockSummaryResponse;
import com.neusoft.smart.pos.inventory.persistent.domain.InventoryCheckStock;
import com.neusoft.smart.pos.inventory.persistent.domain.InventoryCheckStockDtl;
import com.neusoft.smart.pos.inventory.persistent.domain.InventoryCheckStockDtlExample;
import com.neusoft.smart.pos.inventory.persistent.domain.InventoryCheckStockExample;
import com.neusoft.smart.pos.inventory.persistent.domain.InventoryCheckStockSummary;
import com.neusoft.smart.pos.inventory.persistent.mapper.InventoryCheckStockDtlMapper;
import com.neusoft.smart.pos.inventory.persistent.mapper.InventoryCheckStockMapper;
import com.neusoft.smart.pos.inventory.persistent.mapper.InventoryStoreMapper;
import lombok.val;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@EnableTransactionManagement
@Service("inventoryCheckStockService")
public class InventoryCheckStockService {
    
    public final static int STOCK_WIN = 0;
    
    public final static int STOCK_LOSS = 1;
    
    @Resource
    InventoryCheckStockMapper inventoryCheckStockMapper;
    
    @Resource
    InventoryCheckStockDtlMapper inventoryCheckStockDtlMapper;
    
    @Resource
    InventoryStoreMapper inventoryStoreMapper;
    
    @Autowired
    InventoryStoreBatchService inventoryStoreBatchService;
    
    public InventoryCheckStockListResponse getList(InventoryCheckStockListRequest request) {
        InventoryCheckStockExample example = new InventoryCheckStockExample();
        example.createCriteria().andShopIdEqualTo(request.getShopId());
        
        example.setOrderByClause("id desc");
        example.page(request.getOffset(), request.getLimit());
        
        InventoryCheckStockListResponse response = new InventoryCheckStockListResponse();
        response.setTotal(inventoryCheckStockMapper.countByExample(example));
        
        List<InventoryCheckStockListResponse.Entry> data = new ArrayList<>();
        for (InventoryCheckStock record : inventoryCheckStockMapper.selectByExample(example)) {
            InventoryCheckStockListResponse.Entry entry = new InventoryCheckStockListResponse.Entry();
            entry.setId(record.getId());
            entry.setOperator(record.getCreatedBy());
            entry.setTime(record.getCreatedTime());
            
            data.add(entry);
        }
        response.setData(data);
        
        return response;
    }
    
    public InventoryCheckStockDetailResponse getDetail(InventoryCheckStockDetailRequest request) {
        InventoryCheckStockDtlExample example = new InventoryCheckStockDtlExample();
        InventoryCheckStockDtlExample.Criteria criteria = example.createCriteria();
        
        example.setOrderByClause("id desc");
        example.page(request.getOffset(), request.getLimit());
        
        criteria.andMstIdEqualTo(request.getCheckId());
        if (request.getCheckStatus() != null) {
            if (STOCK_WIN == request.getCheckStatus()) {
                criteria.andDiffNumGreaterThan(new BigDecimal(0));
            } else {
                criteria.andDiffNumLessThan(new BigDecimal(0));
            }
        }
        
        InventoryCheckStockDetailResponse response = new InventoryCheckStockDetailResponse();
        response.setTotal(inventoryCheckStockDtlMapper.countByExample(example));
        
        List<InventoryCheckStockDetailResponse.Entry> data = new ArrayList<>();
        for (InventoryCheckStockDtl detail : inventoryCheckStockDtlMapper.selectByExample(example)) {
            InventoryCheckStockDetailResponse.Entry entry = new InventoryCheckStockDetailResponse.Entry();
            entry.setId(detail.getId());
            entry.setStockNum(detail.getStockNum());
            entry.setCheckNum(detail.getCheckNum());
            entry.setDiffNum(detail.getDiffNum());
            entry.setSupplier(detail.getSupplier());
            entry.setRemark(detail.getRemark());
            entry.setGoodsName(detail.getGoodsName());
            entry.setGoodsBarCode(detail.getGoodsBarCode());
            entry.setGoodsCategory(detail.getGoodsCategory());
            entry.setGoodsUnit(detail.getGoodsUnit());
            
            data.add(entry);
        }
        response.setData(data);
        
        return response;
    }
    
    public InventoryCheckStockSummaryResponse summary(Integer checkId) {
        InventoryCheckStockSummary summary = inventoryCheckStockDtlMapper.selectSummary(checkId);
        
        InventoryCheckStockSummaryResponse response = new InventoryCheckStockSummaryResponse();
        BeanUtils.copyProperties(summary, response);
        
        return response;
    }
    
    public void export(Integer shopId, OutputStream os) throws Exception {
        List<String> fieldNames = Arrays.asList("商品ID", "商品条码", "商品名称", "类别", "供应商", "规格", "数量", "盘点数量", "备注");
        
        List<List<Object>> fieldData = inventoryStoreMapper.selectTotalByShopId(shopId).stream().map(storeTotal ->
                (List<Object>) new ArrayList<Object>(Arrays.asList(storeTotal.getGoodsId(),
                storeTotal.getGoodsBarCode(), storeTotal.getGoodsName(),
                storeTotal.getGoodsCategory(), storeTotal.getSupplier(),
                storeTotal.getGoodsUnit(),
                storeTotal.getStorageNum().divide(new BigDecimal(Math.pow(10, storeTotal.getScale())), storeTotal.getScale(), BigDecimal.ROUND_HALF_UP),
                "", ""))).collect(Collectors.toList());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ExcelUtils.exportExcel(fieldNames, fieldData, outputStream);
        os.write(outputStream.toByteArray());
        os.flush();
        os.close();
    }
    
    @Transactional
    public Integer importFile(Integer shopId, String operator, MultipartFile file) throws Exception {
        val wb = WorkbookFactory.create(file.getInputStream());
        //获取Sheet（工作薄）
        val sheet = wb.getSheetAt(0);
        //开始行数
        int firstRowNum = sheet.getFirstRowNum();
        //结束行数
        int lastRowNum = sheet.getLastRowNum();
        
        if (firstRowNum == lastRowNum) {
            throw new BusinessCommonException(InventoryErrorCode.EMPTY_EXCEL);
        } else {
            val firstRow = sheet.getRow(0);
            val firstCellNum = firstRow.getFirstCellNum();
            val lastCellNum = firstRow.getLastCellNum();
            
            List<InventoryCheckStockDtl> list = new ArrayList<>();
            for (int j = firstRowNum + 1; j <= lastRowNum; j++) {
                //获取一行
                Row row = sheet.getRow(j);
                if (row != null && row.getFirstCellNum() != row.getLastCellNum()) {
                    InventoryCheckStockDtl checkStockDetail = new InventoryCheckStockDtl();
                    Boolean rowIgnore = false;
                    for (int k = firstCellNum; k < lastCellNum; k++) {
                        Cell cell = row.getCell(k);
                        
                        if (cell == null) continue;
                        
                        int i = k - firstCellNum;
                        switch (i) {
                            case 0:
                                cell.setCellType(Cell.CELL_TYPE_STRING);
                                checkStockDetail.setGoodsId(Integer.valueOf(cell.getStringCellValue()));
                                break;
                            case 4:
                                cell.setCellType(Cell.CELL_TYPE_STRING);
                                checkStockDetail.setSupplier(cell.getStringCellValue());
                                break;
                            case 6:
                                cell.setCellType(Cell.CELL_TYPE_STRING);
                                if (StringUtils.isEmpty(cell.getStringCellValue())) rowIgnore = true;
                                checkStockDetail.setStockNum(
                                        new BigDecimal(!StringUtils.isEmpty(cell.getStringCellValue()) ? cell.getStringCellValue() : "0"));
                                break;
                            case 7:
                                //cell.setCellType(Cell.CELL_TYPE_NUMERIC);
                                cell.setCellType(Cell.CELL_TYPE_STRING);
                                //checkStockDetail.setCheckNum(new BigDecimal(cell.getNumericCellValue()));
                                if (StringUtils.isEmpty(cell.getStringCellValue())) rowIgnore = true;
                                checkStockDetail.setCheckNum(new BigDecimal(!StringUtils.isEmpty(cell.getStringCellValue()) ? cell.getStringCellValue() : "0"));
                                break;
                            case 8:
                                cell.setCellType(Cell.CELL_TYPE_STRING);
                                checkStockDetail.setRemark(cell.getStringCellValue());
                            
                        }
                    }
                    if (!rowIgnore) list.add(checkStockDetail);
                }
            }
            
            InventoryCheckStock checkStock = new InventoryCheckStock();
            checkStock.setShopId(shopId);
            checkStock.setCreatedTime(new Date());
            checkStock.setCreatedBy(operator);
            
            inventoryCheckStockMapper.insertSelective(checkStock);
            
            list.stream().forEach(record -> {
                record.setMstId(checkStock.getId());
                
                if (record.getCheckNum() != null) {
                    // TODO: update inventory
                    BigDecimal diffNum = record.getCheckNum().subtract(record.getStockNum());
                    
                    int diffNumInt = diffNum.intValue();
                    if (diffNumInt > 0) {
                        PostInventoryStoreBatchRequest request = new PostInventoryStoreBatchRequest();
                        request.setGoodsId(record.getGoodsId());
                        request.setShopId(shopId);
                        request.setUniqId(String.valueOf(checkStock.getId()));
                        request.setStorageNum(diffNum);//fixme 此处需要更改精度换算
                        request.setOperator(operator);
                        inventoryStoreBatchService.bindGoodsInfoForRequest(request);
                        inventoryStoreBatchService.increaseInventory(request, InventoryConstant.STOCK_ADD, InventoryConstant.CHECK_ADD);
                    } else if (diffNumInt != 0) {
                        PutInventoryStoreBatchRequest request = new PutInventoryStoreBatchRequest();
                        request.setGoodsId(record.getGoodsId());
                        request.setShopId(shopId);
                        request.setUniqId(String.valueOf(checkStock.getId()));
                        request.setStorageNum(diffNum.negate());
                        request.setOperator(operator);
                        inventoryStoreBatchService.reduceInventory(request, InventoryConstant.STOCK_DEC, InventoryConstant.CHECK_DEC);
                    }
                    
                    record.setDiffNum(diffNum);
                }
            });
            
            inventoryCheckStockDtlMapper.batchInsert(list);
            
            return checkStock.getId();
        }
    }
}
