package cn.ysk.cashier.cons.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.ysk.cashier.cons.domain.*;
import cn.ysk.cashier.cons.repository.TbConsInfoFlowRepository;
import cn.ysk.cashier.cons.repository.TbConsInfoRepository;
import cn.ysk.cashier.cons.repository.TbConsSuppFlowRepository;
import cn.ysk.cashier.cons.repository.TbConsTypeRepository;
import cn.ysk.cashier.cons.service.TbConsInfoService;
import cn.ysk.cashier.cons.service.dto.TbConsInfoDto;
import cn.ysk.cashier.cons.service.dto.TbConsInfoQueryCriteria;
import cn.ysk.cashier.cons.service.mapstruct.TbConsInfoMapper;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.pojo.product.TbProductStockOperate;
import cn.ysk.cashier.pojo.shop.TbShopInfo;
import cn.ysk.cashier.pojo.shop.TbShopPurveyor;
import cn.ysk.cashier.pojo.shop.TbShopPurveyorTransact;
import cn.ysk.cashier.repository.product.TbProductStockOperateRepository;
import cn.ysk.cashier.repository.shop.TbShopInfoRepository;
import cn.ysk.cashier.repository.shop.TbShopPurveyorRepository;
import cn.ysk.cashier.repository.shop.TbShopPurveyorTransactRepository;
import cn.ysk.cashier.utils.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.*;

import static cn.ysk.cashier.utils.StringCodeUtil.TYPE.LETTER_CAPITAL_NUMBER;

/**
 * @author admin
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2024-06-22
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class TbConsInfoServiceImpl implements TbConsInfoService {

    public final TbConsTypeRepository tbConsTypeRepository;
    public final TbConsInfoFlowRepository tbConsInfoFlowRepository;
    public final TbConsSuppFlowRepository tbConsSuppFlowRepository;
    private final TbConsInfoRepository tbConsInfoRepository;
    private final TbConsInfoMapper tbConsInfoMapper;
    private final TbShopPurveyorRepository tbShopPurveyorRepository;

    private final TbShopInfoRepository tbShopInfoRepository;


    private final TbShopPurveyorTransactRepository purveyorTransactRepository;


    private final TbProductStockOperateRepository tbProductStockOperateRepository;



    @Override
    public Map<String, Object> queryAll(TbConsInfoQueryCriteria criteria, Pageable pageable) {
        Page<TbConsInfo> page = tbConsInfoRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(tbConsInfoMapper::toDto));
    }

    @Override
    public List<TbConsInfoDto> queryAll(TbConsInfoQueryCriteria criteria) {

        List<TbConsInfoDto> list = tbConsInfoMapper.toDto(tbConsInfoRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
        if (Objects.nonNull(list) && list.size() > 0) {
            list.parallelStream().forEach(it -> {
                it.setContypeCode(tbConsTypeRepository.findById(it.getConTypeId()).orElseGet(TbConsType::new).getConTypeCode());
            });
        }
        return list;
    }

    @Override
    @Transactional
    public TbConsInfoDto findById(Integer id) {
        TbConsInfo tbConsInfo = tbConsInfoRepository.findById(id).orElseGet(TbConsInfo::new);
        ValidationUtil.isNull(tbConsInfo.getId(), "TbConsInfo", "id", id);
        return tbConsInfoMapper.toDto(tbConsInfo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TbConsInfoDto create(List<TbConsInfo> resources) throws Exception {


        for (TbConsInfo resource : resources) {
            TbConsType tbConsType = tbConsTypeRepository.getById(resource.getConTypeId());
            if (Objects.isNull(tbConsType)) {
                throw new Exception("不存在的耗材类型");
            }

//        int count = tbConsInfoRepository.countByConCode(resources.getConCode());
//        if (count > 0) {
//            throw new Exception("耗材代码不允许重复");
//        }


            resource.setConCode(StringCodeUtil.getRandom(8, LETTER_CAPITAL_NUMBER));
            resource.setConTypeName(tbConsType.getConTypeName());
            resource.setLasterInStock(BigDecimal.ZERO);
            resource.setStockNumber(BigDecimal.ZERO);
            resource.setStatus("1");
            resource.setStockConsume(BigDecimal.ZERO);
            resource.setCreateTime(new Timestamp(System.currentTimeMillis()));
            resource = tbConsInfoRepository.save(resource);


            TbConsInfoFlow flow = new TbConsInfoFlow();

            flow.setBizCode("stockIn");
            flow.setBizName("初始化入库");
            flow.setBizType("+");
            flow.setConsId(resource.getId());
            flow.setShopId(resource.getShopId());
            flow.setConName(resource.getConName());
            flow.setAmount(BigDecimal.ZERO);
            flow.setBalance(BigDecimal.ZERO);

            flow.setCreateTime(new Timestamp(System.currentTimeMillis()));
            tbConsInfoFlowRepository.save(flow);


        }
        return tbConsInfoMapper.toDto(new TbConsInfo());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(List<TbConsInfo> resources) throws Exception {
        for (TbConsInfo resource : resources) {
            TbConsInfo tbConsInfo = tbConsInfoRepository.findById(resource.getId()).orElseGet(TbConsInfo::new);

            if (Objects.isNull(tbConsInfo)) {
                throw new Exception("耗材信息不存在");
            }

            tbConsInfo.setConName(ObjectUtil.isEmpty(resource.getConName())||ObjectUtil.isNull(resource.getConName())?tbConsInfo.getConName():resource.getConName());
            tbConsInfo.setPrice(ObjectUtil.isEmpty(resource.getPrice())||ObjectUtil.isNull(resource.getPrice())?tbConsInfo.getPrice():resource.getPrice());
            tbConsInfo.setConUnit(ObjectUtil.isEmpty(resource.getConUnit())||ObjectUtil.isNull(resource.getConUnit())?tbConsInfo.getConUnit(): resource.getConUnit());
            tbConsInfo.setConWarning(ObjectUtil.isEmpty(resource.getConWarning())||ObjectUtil.isNull(resource.getConWarning())?tbConsInfo.getConWarning():resource.getConWarning());
            tbConsInfo.setStatus(ObjectUtil.isEmpty(resource.getStatus())||ObjectUtil.isNull(resource.getStatus())?tbConsInfo.getStatus():resource.getStatus());
            tbConsInfo.setIsCheck(ObjectUtil.isEmpty(resource.getIsCheck())||ObjectUtil.isNull(resource.getIsCheck())?tbConsInfo.getIsCheck():resource.getIsCheck());
            tbConsInfo.setUpdateTime(new Timestamp(System.currentTimeMillis()));

            tbConsInfoRepository.save(tbConsInfo);
        }

    }

    @Override
    public void deleteAll(Integer[] ids) {
        for (Integer id : ids) {
            tbConsInfoRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<TbConsInfoDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (TbConsInfoDto tbConsInfo : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("耗材代码", tbConsInfo.getConCode());
            map.put("耗材名称", tbConsInfo.getConName());
            map.put("耗材类型代码", tbConsInfo.getContypeCode());
            map.put("单位", tbConsInfo.getConUnit());
            map.put("价格", tbConsInfo.getPrice());
            map.put("耗材预警值", tbConsInfo.getConWarning());
            map.put("库存", tbConsInfo.getStockNumber().subtract(tbConsInfo.getStockConsume()));
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stockInOut(SuppFlow resources) throws Exception {
        log.info("resources:{}", JSON.toJSONString(resources));
        if (Objects.isNull(resources)) {
            throw new Exception("参数错误");
        }

        TbShopInfo tbShopInfo = tbShopInfoRepository.getById(resources.getShopId());
        if (Objects.isNull(tbShopInfo)) {
            throw new Exception("店铺信息不存在");
        }

        TbShopPurveyor purveyor = null;
        TbProductStockOperate stockOperate = new TbProductStockOperate();
        if (Objects.nonNull(resources.getSupplierId())) {
            purveyor = tbShopPurveyorRepository.getById(resources.getSupplierId());
            if (Objects.nonNull(purveyor)) {
                stockOperate.setPurveyorId(resources.getSupplierId().toString());
                stockOperate.setPurveyorName(purveyor.getPurveyorName());
            }
        }

        stockOperate.setShopId(tbShopInfo.getId().toString());
        stockOperate.setStockSnap("");
        stockOperate.setType(resources.getType().equals("in") ? "cons_in" : "cons_out");
        Map<String, String> operatorSnapMap = new HashMap<>();
        operatorSnapMap.put("name", tbShopInfo.getShopName());
        operatorSnapMap.put("account", tbShopInfo.getAccount());

        stockOperate.setOperatorSnap(JSON.toJSONString(operatorSnapMap));
        stockOperate.setRemark(resources.getRemark());
        stockOperate.setStockTime(System.currentTimeMillis());
        stockOperate.setCreatedAt(System.currentTimeMillis());
        stockOperate.setUpdatedAt(System.currentTimeMillis());
        stockOperate.setStatus("normal");

        JSONArray array = new JSONArray();


        for (SuppFlow.ConInfos conInfos : resources.getList()) {
            JSONObject object = new JSONObject();

            TbConsInfo info = tbConsInfoRepository.getById(conInfos.getConInfold());
            if (Objects.isNull(info)) {
                log.info("耗材信息不存在");
                continue;
            }

            TbConsSuppFlow suppFlow = new TbConsSuppFlow();

            TbConsInfoFlow flow = new TbConsInfoFlow();

            TbShopPurveyorTransact purveyorTransact = new TbShopPurveyorTransact();
            purveyorTransact.setShopId(tbShopInfo.getId().toString());
            purveyorTransact.setPurveyorName(Objects.isNull(purveyor) ? "" : purveyor.getPurveyorName());
            purveyorTransact.setPurveyorId(Objects.isNull(purveyor) ? "" : purveyor.getId().toString());
            purveyorTransact.setRemark(resources.getRemark());
            purveyorTransact.setCreatedAt(System.currentTimeMillis());
            purveyorTransact.setUpdatedAt(System.currentTimeMillis());

            suppFlow.setConInfoId(info.getConTypeId());
            suppFlow.setShopId(resources.getShopId());
            suppFlow.setSupplierId(Objects.isNull(resources.getSupplierId()) ? 0 : resources.getSupplierId());
            suppFlow.setType(resources.getType());
            suppFlow.setStockNumber(conInfos.getStockNumber());

            BigDecimal amount = info.getStockNumber().subtract(info.getStockConsume());


            if ("in".equals(resources.getType())) {
                stockOperate.setSubType(1);
                info.setStockNumber(info.getStockNumber().add(conInfos.getStockNumber()));
                info.setLasterInStock(conInfos.getStockNumber());

                suppFlow.setBalance(info.getStockNumber().subtract(info.getStockConsume()).add(conInfos.getStockNumber()));


                flow.setBizCode("stockIn");
                flow.setBizName("耗材入库");
                flow.setBizType("+");

                purveyorTransact.setTotalAmount(resources.getAccountsPayable());
                purveyorTransact.setPaidAmount(resources.getActualPayment());
                purveyorTransact.setWaitAmount((resources.getAccountsPayable().subtract(resources.getActualPayment())));
                purveyorTransact.setType("cons_in");

                object.put("number", conInfos.getStockNumber());
            } else if ("out".equals(resources.getType())) {
                stockOperate.setSubType(-1);

                if (conInfos.getStockNumber().compareTo(info.getStockNumber()) > 0) {
                    throw new BadRequestException("出库数量大于现有的库存数量");
                }

                info.setStockNumber(info.getStockNumber().subtract(conInfos.getStockNumber()));


                suppFlow.setBalance(info.getStockNumber().subtract(info.getStockConsume()).subtract(conInfos.getStockNumber()));


                flow.setBizCode("stockout");
                flow.setBizName("耗材出库");
                flow.setBizType("-");

                purveyorTransact.setPaidAmount(resources.getActualPayment());
                purveyorTransact.setTotalAmount(resources.getAccountsPayable().negate());

                purveyorTransact.setWaitAmount((resources.getAccountsPayable().subtract(resources.getActualPayment())).negate());
                purveyorTransact.setType("cons_out");
                object.put("number", conInfos.getStockNumber());
            } else {
                throw new BadRequestException("错误操作类型");
            }


            object.put("stockNumber", amount);

            object.put("name", info.getConName());
            object.put("unitName", info.getConUnit());
            array.add(object);

            suppFlow.setPrice(conInfos.getPrice());
            suppFlow.setAmount(conInfos.getStockNumber());
            suppFlow.setAccountsPayable(resources.getAccountsPayable());
            suppFlow.setActualPayment(resources.getActualPayment());
            suppFlow.setCreateTime(new Timestamp(System.currentTimeMillis()));


            if (resources.getAccountsPayable().compareTo(resources.getActualPayment()) <= 0) {
                purveyorTransact.setStatus(1);
            } else {
                purveyorTransact.setStatus(0);
            }

            purveyorTransact.setPaidAt(System.currentTimeMillis());

            purveyorTransactRepository.save(purveyorTransact);
            if (resources.getSupplierId() != null) {
                tbShopPurveyorRepository.upLastTransactAt(resources.getSupplierId().toString());
            }


            tbConsInfoRepository.save(info);
            tbConsSuppFlowRepository.save(suppFlow);


            flow.setConsId(info.getId());
            flow.setShopId(info.getShopId());
            flow.setConName(info.getConName());
            flow.setAmount(conInfos.getStockNumber());
            flow.setBalance(info.getStockNumber().subtract(info.getStockConsume()));

            flow.setCreateTime(new Timestamp(System.currentTimeMillis()));
            tbConsInfoFlowRepository.save(flow);
        }

        stockOperate.setBatchNumber("");
        stockOperate.setStockSnap(array.toJSONString());
        tbProductStockOperateRepository.save(stockOperate);
    }

    @Override
    @Transactional
    public void inputStock(String shopId, MultipartFile file) throws Exception {


        //根据路径获取这个操作excel的实例
        XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file.getInputStream());
        //根据页面index 获取sheet页
        XSSFSheet sheet = xssfWorkbook.getSheetAt(0);
        XSSFRow row = null;

        List<TbConsInfo> consInfos = new ArrayList<>();
        for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
            row = sheet.getRow(i);
            if (row != null) {
                XSSFCell cell0 = row.getCell(0);
                XSSFCell cell1 = row.getCell(1);
                if (ObjectUtil.isEmpty(cell1)) {
                    log.info("耗材名称为空:{}", cell1);
                    continue;
                }


                XSSFCell cell2 = row.getCell(2);

                if (ObjectUtil.isEmpty(cell2)) {
                    log.info("耗材类型为空:{}", cell2);
                    continue;
                }


                XSSFCell cell3 = row.getCell(3);

                if (ObjectUtil.isEmpty(cell3)) {
                    log.info("耗材单位为空:{}", cell3);
                    continue;
                }


                XSSFCell cell4 = row.getCell(4);

                if (ObjectUtil.isEmpty(cell4)) {
                    log.info("耗材价格为空:{}", cell4);
                    continue;
                }


                XSSFCell cell5 = row.getCell(5);

                if (ObjectUtil.isEmpty(cell5)) {
                    log.info("耗材预警值为空:{}", cell5);
                    continue;
                }


                XSSFCell cell6 = row.getCell(6);

                if (ObjectUtil.isEmpty(cell6)) {
                    log.info("耗材库存为空:{}", cell6);
                    continue;
                }


                TbConsType tbConsType = tbConsTypeRepository.findByConTypeCode(cell2.toString());
                if (Objects.isNull(tbConsType)) {
                    log.info("不存在的耗材类型：{}", cell2.toString());
                    continue;
                }


                if (ObjectUtil.isNotEmpty(cell0)) {

                    TbConsInfo consInfo = tbConsInfoRepository.findByConCode(cell0.toString());
                    if (ObjectUtil.isEmpty(consInfo) || ObjectUtil.isNull(consInfo)) {
                        consInfo = new TbConsInfo();
                        consInfo.setConCode(StringCodeUtil.getRandom(8, LETTER_CAPITAL_NUMBER));
                        consInfo.setShopId(Integer.valueOf(shopId));
                        consInfo.setConTypeId(tbConsType.getId());
                        consInfo.setConTypeName(tbConsType.getConTypeName());
                        consInfo.setConName(cell1.toString());
                        consInfo.setPrice(new BigDecimal(cell4.toString()));
                        consInfo.setStockNumber(new BigDecimal(cell6.toString()));
                        consInfo.setStockConsume(BigDecimal.ZERO);
                        consInfo.setStatus("1");
                        consInfo.setConUnit(cell3.toString());
                        consInfo.setLasterInStock(consInfo.getStockNumber());
                        consInfo.setConWarning(new BigDecimal(cell5.toString()));
                        consInfo.setCreateTime(new Timestamp(System.currentTimeMillis()));
                        consInfo = tbConsInfoRepository.save(consInfo);


                        TbConsInfoFlow flow = new TbConsInfoFlow();
                        flow.setBizCode("stockOtherIn");
                        flow.setBizName("导入库存");
                        flow.setBizType("+");
                        flow.setConsId(consInfo.getId());
                        flow.setShopId(consInfo.getShopId());
                        flow.setConName(consInfo.getConName());
                        flow.setAmount(consInfo.getStockNumber());
                        flow.setBalance(consInfo.getStockNumber());

                        flow.setCreateTime(new Timestamp(System.currentTimeMillis()));
                        tbConsInfoFlowRepository.save(flow);


                    } else {
                        consInfo.setConName(cell1.toString());
                        consInfo.setPrice(new BigDecimal(cell4.toString()));
                        consInfo.setStockNumber(new BigDecimal(cell6.toString()));
                        consInfo.setStockConsume(BigDecimal.ZERO);
                        consInfo.setConUnit(cell3.toString());
                        consInfo.setLasterInStock(consInfo.getStockNumber());
                        consInfo.setConWarning(new BigDecimal(cell5.toString()));
                        consInfo.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                        consInfo = tbConsInfoRepository.save(consInfo);

                        TbConsInfoFlow flow = new TbConsInfoFlow();
                        flow.setBizCode("stockOtherIn");
                        flow.setBizName("导入库存");
                        flow.setBizType("+");
                        flow.setConsId(consInfo.getId());
                        flow.setShopId(consInfo.getShopId());
                        flow.setConName(consInfo.getConName());
                        flow.setAmount(consInfo.getStockNumber());
                        flow.setBalance(consInfo.getStockNumber());

                        flow.setCreateTime(new Timestamp(System.currentTimeMillis()));
                        tbConsInfoFlowRepository.save(flow);
                    }
                } else {
                    TbConsInfo consInfo = new TbConsInfo();
                    consInfo.setConCode(StringCodeUtil.getRandom(8, LETTER_CAPITAL_NUMBER));
                    consInfo.setShopId(Integer.valueOf(shopId));
                    consInfo.setConTypeId(tbConsType.getId());
                    consInfo.setConTypeName(tbConsType.getConTypeName());
                    consInfo.setConName(cell1.toString());
                    consInfo.setPrice(new BigDecimal(cell4.toString()));
                    consInfo.setStockNumber(new BigDecimal(cell6.toString()));
                    consInfo.setStockConsume(BigDecimal.ZERO);
                    consInfo.setStatus("1");
                    consInfo.setConUnit(cell3.toString());
                    consInfo.setLasterInStock(consInfo.getStockNumber());
                    consInfo.setConWarning(new BigDecimal(cell5.toString()));
                    consInfo.setCreateTime(new Timestamp(System.currentTimeMillis()));
                    consInfo = tbConsInfoRepository.save(consInfo);


                    TbConsInfoFlow flow = new TbConsInfoFlow();
                    flow.setBizCode("stockOtherIn");
                    flow.setBizName("导入库存");
                    flow.setBizType("+");
                    flow.setConsId(consInfo.getId());
                    flow.setShopId(consInfo.getShopId());
                    flow.setConName(consInfo.getConName());
                    flow.setAmount(consInfo.getStockNumber());
                    flow.setBalance(consInfo.getStockNumber());

                    flow.setCreateTime(new Timestamp(System.currentTimeMillis()));
                    tbConsInfoFlowRepository.save(flow);

                }
            }

        }

    }

    public static void main(String[] args) {
        System.out.println(UUID.randomUUID().toString());
    }

}
