package vn.azteam.tpf.service.impl;

import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vn.azteam.tpf.service.ReportInOutMaterialService;
import vn.azteam.tpf.service.dto.*;
import vn.azteam.tpf.service.util.ExportExcelUtil;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Period;
import java.util.*;

@Service
@Transactional
public class ReportInOutMaterialServiceImpl implements ReportInOutMaterialService {
    private final Logger log = LoggerFactory.getLogger(ReportsServiceImpl.class);
    private final String EXPORT_DATE_FORMAT = "dd/MM/yyyy";
    private final String REPORT_IN_OUT_MATERIAL_TEMPLATE_PATH = "classpath:templates/report/reportInOutMaterials.xlsx";
    private final String REPORT_FINISH_GOODS_TEMPLATE_PATH = "classpath:templates/report/reportFinishedGoodsWarehouse.xlsx";
    private final String REPORT_FARM_RESULT_TEMPLATE_PATH = "classpath:templates/report/reportFarmingResults.xlsx";

    private final EntityManager em;
    private final ResourceLoader resourceLoader;
    private final ExportExcelUtil exportExcelUtil;


    public ReportInOutMaterialServiceImpl(EntityManager em, ResourceLoader resourceLoader, ExportExcelUtil exportExcelUtil) {
        this.em = em;
        this.resourceLoader = resourceLoader;
        this.exportExcelUtil = exportExcelUtil;
    }

    private List<Tuple> getReportInOutMaterialsData(Long customerId, Long suppliesTypeId, Date startDate, Date endDate) {
        String sqlString = "";
        DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
        String currentDateTime = dateFormatter.format(startDate) + " 23:59:59";

        String startDateTime = dateFormatter.format(startDate) + " 00:00:00";
        String endDateTime = dateFormatter.format(endDate) + " 23:59:59";
        if (suppliesTypeId != null) {
            sqlString = "SELECT \n" +
                " tbbdl.id, tbbdl.tb_block_details_id, tbst.name as suppliesTypeName, tbbdl.code, tbbdl.quantity,\n" +
                " SUM(CASE WHEN  tbbdl.tb_block_details_id=tbsu.tb_block_details_id AND tbsu.update_date >= :startUpdateDate AND tbsu.update_date <= :endUpdateDate  AND tbsu.action=0  THEN tbsu.quantity ELSE 0 END)  as quantityImport, \n" +
                " SUM(CASE WHEN tbbdl.tb_block_details_id=tbsu.tb_block_details_id AND tbsu.update_date >= :startUpdateDate AND tbsu.update_date <= :endUpdateDate AND tbsu.action=1  THEN tbsu.quantity ELSE 0 END) as quantityExport\n" +
                " FROM tb_block_details_report tbbdl\n" +
                " INNER JOIN (SELECT MAX(pi.id) AS maxid\n" +
                "            FROM tb_block_details_report pi GROUP BY pi.tb_block_details_id) tbbdl1\n" +
                " ON (tbbdl.id = tbbdl1.maxid)\n" +
                " INNER JOIN tb_supplies_in_warehouse as tbsiw ON (tbsiw.id = tbbdl.tb_supplies_in_warehouse_id)\n" +
                " INNER JOIN tb_entity as tbe ON (tbe.id = tbsiw.tb_entity_id)\n" +
                " INNER JOIN tb_supplies_type as tbst ON (tbst.id = tbbdl.tb_supplies_type_id)\n" +
                " INNER JOIN tb_stock_update as tbsu ON (tbsu.tb_block_details_id =tbbdl.tb_block_details_id)\n" +
                " WHERE tbe.tb_customer_id = :customerId \n" +
                " AND tbbdl.tb_supplies_type_id= :suppliesTypeId\n" +
                " GROUP BY tbbdl.id,tbbdl.tb_block_details_id;";
            Query query = em
                .createNativeQuery(sqlString, Tuple.class)
                .setParameter("customerId", customerId)
                .setParameter("suppliesTypeId", suppliesTypeId)
                .setParameter("startUpdateDate", startDateTime)
                .setParameter("endUpdateDate", endDateTime);

            return query.getResultList();
        } else {
            sqlString = "SELECT \n" +
                " tbbdl.id, tbbdl.tb_block_details_id, tbst.name as suppliesTypeName, tbbdl.code, tbbdl.quantity,\n" +
                " SUM(CASE WHEN  tbbdl.tb_block_details_id=tbsu.tb_block_details_id AND tbsu.update_date >= :startUpdateDate AND tbsu.update_date <= :endUpdateDate  AND tbsu.action=0  THEN tbsu.quantity ELSE 0 END)  as quantityImport, \n" +
                " SUM(CASE WHEN tbbdl.tb_block_details_id=tbsu.tb_block_details_id AND tbsu.update_date >= :startUpdateDate AND tbsu.update_date <= :endUpdateDate AND tbsu.action=1  THEN tbsu.quantity ELSE 0 END) as quantityExport\n" +
                " FROM tb_block_details_report tbbdl\n" +
                " INNER JOIN (SELECT MAX(pi.id) AS maxid\n" +
                "            FROM tb_block_details_report pi GROUP BY pi.tb_block_details_id) tbbdl1\n" +
                " ON (tbbdl.id = tbbdl1.maxid)\n" +
                " INNER JOIN tb_supplies_in_warehouse as tbsiw ON (tbsiw.id = tbbdl.tb_supplies_in_warehouse_id)\n" +
                " INNER JOIN tb_entity as tbe ON (tbe.id = tbsiw.tb_entity_id)\n" +
                " INNER JOIN tb_supplies_type as tbst ON (tbst.id = tbbdl.tb_supplies_type_id)\n" +
                " INNER JOIN tb_stock_update as tbsu ON (tbsu.tb_block_details_id =tbbdl.tb_block_details_id)\n" +
                " WHERE tbe.tb_customer_id = :customerId \n" +
                " GROUP BY tbbdl.id,tbbdl.tb_block_details_id;";

            Query query = em
                .createNativeQuery(sqlString, Tuple.class)
                .setParameter("customerId", customerId)
                .setParameter("startUpdateDate", startDateTime)
                .setParameter("endUpdateDate", endDateTime);

            return query.getResultList();
        }

    }

    private void fillReportInOutMaterialsData(
        XSSFSheet sheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        List<Tuple> data) {
        int sequenceNo = 0;
        int rowIdx = 1; // after header row
        for (Tuple item : data) {
            rowIdx++;
            sequenceNo++;
            Row row = sheet.createRow(rowIdx);
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 0, sequenceNo);
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 1, "suppliesTypeName");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 2, "code");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 3, "quantity");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 4, "quantityImport");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 5, "quantityExport");
            TBBlockDetailsReportDTO tbBlockDetailsReportDTO = convertInOut(item);
            Double endQuantity = (tbBlockDetailsReportDTO.getQuantity() + tbBlockDetailsReportDTO.getQuantityImport()) - tbBlockDetailsReportDTO.getQuantityExport();
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 6, endQuantity);
        }
    }

    @Override
    public void downloadReportInOutMaterials(Long customerId, Long suppliesTypeId, Date startDate, Date endDate, HttpServletResponse response) throws IOException {
        if (endDate == null) {
            endDate = Date.from(Instant.now());
        }
        Resource resource = resourceLoader.getResource(REPORT_IN_OUT_MATERIAL_TEMPLATE_PATH);
        XSSFWorkbook workbook = new XSSFWorkbook(resource.getInputStream());
        XSSFSheet sheet = workbook.getSheetAt(0);
        if (null == sheet) return;
        CellStyle datetimeCellStyle = exportExcelUtil.createBorderCellStyle(workbook);
        CreationHelper createHelper = workbook.getCreationHelper();
        datetimeCellStyle.setDataFormat(createHelper.createDataFormat().getFormat(EXPORT_DATE_FORMAT));
        CellStyle borderCellStyle = exportExcelUtil.createBorderCellStyle(workbook);

        List<Tuple> importedMaterialData = getReportInOutMaterialsData(customerId, suppliesTypeId, startDate, endDate);
        fillReportInOutMaterialsData(sheet, datetimeCellStyle, borderCellStyle, importedMaterialData);

        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.close();
    }

    public TBBlockDetailsReportDTO convertInOut(Tuple data) {
        TBBlockDetailsReportDTO tbBlockDetailsReportDTO = new TBBlockDetailsReportDTO();
        Long id = ((BigInteger) data.get("id")).longValue();
        tbBlockDetailsReportDTO.setId(id);
        Double quantity = Double.valueOf((Double) data.get("quantity"));
        tbBlockDetailsReportDTO.setQuantity(quantity);
        Double quantityExport = Double.valueOf((Double) data.get("quantityExport"));
        tbBlockDetailsReportDTO.setQuantityExport(quantityExport);
        Double quantityImport = Double.valueOf((Double) data.get("quantityImport"));
        tbBlockDetailsReportDTO.setQuantityImport(quantityImport);
        String name = String.valueOf(data.get("suppliesTypeName"));
        tbBlockDetailsReportDTO.setSuppliesTypeName(name);
        String code = String.valueOf(data.get("code"));
        tbBlockDetailsReportDTO.setCode(code);
        return tbBlockDetailsReportDTO;
    }

    @Override
    public Page<TBBlockDetailsReportDTO> reportInOutMaterials(Long customerId, Long suppliesTypeId, Date startDate, Date endDate, Pageable pageable) {
        List<Tuple> data = getReportInOutMaterialsData(customerId, suppliesTypeId, startDate, endDate);
        List<TBBlockDetailsReportDTO> logDTOList = new ArrayList<>();
        for (Tuple item : data) {
            TBBlockDetailsReportDTO tbBlockDetailsLogDTO = convertInOut(item);
            logDTOList.add(tbBlockDetailsLogDTO);
        }
        final int start = (int) pageable.getOffset();
        final int end = Math.min((start + pageable.getPageSize()), logDTOList.size());
        final Page<TBBlockDetailsReportDTO> page = new PageImpl<>(logDTOList.subList(start, end), pageable, logDTOList.size());
        return page;
    }

    private List<Tuple> getReportFinishedGoodsWarehouseData(Long customerId, Long cropId, Date startDate, Date endDate) {
        String sqlString = "";
        DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
        String currentDateTime = dateFormatter.format(startDate) + " 23:59:59";

        String startDateTime = dateFormatter.format(startDate) + " 00:00:00";
        String endDateTime = dateFormatter.format(endDate) + " 23:59:59";
        if (cropId != null) {
            sqlString = "SELECT \n" +
                " tbpbdr.id, tbpbdr.product_level_1, tbpbdr.product_level_2, tbpbdr.product_level_3, tbpbdr.code,\n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=0 AND tbpbd.product_level=1 THEN tbsu.quantity ELSE 0 END)  as quantityImportLevel1, \n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=1 AND tbpbd.product_level=1 THEN tbsu.quantity ELSE 0 END) as quantityExportLevel1,\n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=0 AND tbpbd.product_level=2 THEN tbsu.quantity ELSE 0 END)  as quantityImportLevel2, \n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=1 AND tbpbd.product_level=2 THEN tbsu.quantity ELSE 0 END) as quantityExportLevel2,\n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=0 AND tbpbd.product_level=3 THEN tbsu.quantity ELSE 0 END)  as quantityImportLevel3, \n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=1 AND tbpbd.product_level=3 THEN tbsu.quantity ELSE 0 END) as quantityExportLevel3\n" +
                " FROM tb_product_block_details_report tbpbdr\n" +
                " INNER JOIN (SELECT MAX(pi.id) AS maxid FROM tb_product_block_details_report pi GROUP BY pi.tb_crop_id) tbpbdr1\n" +
                " ON (tbpbdr.id = tbpbdr1.maxid)\n" +
                " INNER JOIN tb_product_block_details as tbpbd ON (tbpbd.tb_crop_id = tbpbdr.tb_crop_id)\n" +
                " INNER JOIN tb_product_stock_update as tbpsu ON (tbpsu.tb_product_block_Details_id = tbpbd.id)\n" +
                " INNER JOIN tb_entity as tbe ON (tbe.id = tbpbd.tb_entity_id)\n" +
                " WHERE tbe.tb_customer_id = :customerId\n" +
                " AND tbpbdr.tb_crop_id= :cropId\n" +
                " GROUP BY tbpbdr.id,tbpbdr.tb_crop_id;";
            Query query = em
                .createNativeQuery(sqlString, Tuple.class)
                .setParameter("customerId", customerId)
                .setParameter("cropId", cropId)
                .setParameter("startUpdateDate", startDateTime)
                .setParameter("endUpdateDate", endDateTime);

            return query.getResultList();
        } else {
            sqlString = "SELECT \n" +
                " tbpbdr.id, tbpbdr.product_level_1 as productLevel1, tbpbdr.product_level_2 as productLevel2, tbpbdr.product_level_3 as productLevel3, tbpbdr.code,\n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=0 AND tbpbd.product_level=1 THEN tbsu.quantity ELSE 0 END)  as quantityImportLevel1, \n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=1 AND tbpbd.product_level=1 THEN tbsu.quantity ELSE 0 END) as quantityExportLevel1,\n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=0 AND tbpbd.product_level=2 THEN tbsu.quantity ELSE 0 END)  as quantityImportLevel2, \n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=1 AND tbpbd.product_level=2 THEN tbsu.quantity ELSE 0 END) as quantityExportLevel2,\n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=0 AND tbpbd.product_level=3 THEN tbsu.quantity ELSE 0 END)  as quantityImportLevel3, \n" +
                " SUM(CASE WHEN tbpsu.update_date >= :startUpdateDate AND tbpsu.update_date <= :endUpdateDate AND tbpsu.action=1 AND tbpbd.product_level=3 THEN tbsu.quantity ELSE 0 END) as quantityExportLevel3\n" +
                " FROM tb_product_block_details_report tbpbdr\n" +
                " INNER JOIN (SELECT MAX(pi.id) AS maxid FROM tb_product_block_details_report pi GROUP BY pi.tb_crop_id) tbpbdr1\n" +
                " ON (tbpbdr.id = tbpbdr1.maxid)\n" +
                " INNER JOIN tb_product_block_details as tbpbd ON (tbpbd.tb_crop_id = tbpbdr.tb_crop_id)\n" +
                " INNER JOIN tb_product_stock_update as tbpsu ON (tbpsu.tb_product_block_Details_id = tbpbd.id)\n" +
                " INNER JOIN tb_entity as tbe ON (tbe.id = tbpbd.tb_entity_id)\n" +
                " WHERE tbe.tb_customer_id = :customerId\n" +
                " GROUP BY tbpbdr.id,tbpbdr.tb_crop_id;";

            Query query = em
                .createNativeQuery(sqlString, Tuple.class)
                .setParameter("customerId", customerId)
                .setParameter("startUpdateDate", startDateTime)
                .setParameter("endUpdateDate", endDateTime);

            return query.getResultList();
        }

    }

    public TBProductBlockDetailsReportDTO convertFinishGoods(Tuple data) {
        TBProductBlockDetailsReportDTO tbProductBlockDetailsReportDTO = new TBProductBlockDetailsReportDTO();
        Double productLevel1 = Double.valueOf((Double) data.get("productLevel1"));
        tbProductBlockDetailsReportDTO.setProductLevel1(productLevel1);
        Double quantityImportLevel1 = Double.valueOf((Double) data.get("quantityImportLevel1"));
        tbProductBlockDetailsReportDTO.setQuantityImportLevel1(quantityImportLevel1);
        Double quantityExportLevel1 = Double.valueOf((Double) data.get("quantityExportLevel1"));
        tbProductBlockDetailsReportDTO.setQuantityExportLevel1(quantityExportLevel1);
        Double endProductLevel1 = (productLevel1 + quantityImportLevel1) - quantityExportLevel1;
        tbProductBlockDetailsReportDTO.setEndProductLevel1(endProductLevel1);

        Double productLevel2 = Double.valueOf((Double) data.get("productLevel2"));
        tbProductBlockDetailsReportDTO.setProductLevel2(productLevel2);
        Double quantityImportLevel2 = Double.valueOf((Double) data.get("quantityImportLevel2"));
        tbProductBlockDetailsReportDTO.setQuantityImportLevel2(quantityImportLevel2);
        Double quantityExportLevel2 = Double.valueOf((Double) data.get("quantityExportLevel3"));
        tbProductBlockDetailsReportDTO.setQuantityExportLevel2(quantityExportLevel2);
        Double endProductLevel2 = (productLevel2 + quantityImportLevel2) - quantityExportLevel2;
        tbProductBlockDetailsReportDTO.setEndProductLevel2(endProductLevel2);

        Double productLevel3 = Double.valueOf((Double) data.get("productLevel3"));
        tbProductBlockDetailsReportDTO.setProductLevel3(productLevel3);
        Double quantityImportLevel3 = Double.valueOf((Double) data.get("quantityImportLevel3"));
        tbProductBlockDetailsReportDTO.setQuantityImportLevel3(quantityImportLevel3);
        Double quantityExportLevel3 = Double.valueOf((Double) data.get("quantityExportLevel3"));
        tbProductBlockDetailsReportDTO.setQuantityExportLevel3(quantityExportLevel3);
        Double endProductLevel3 = (productLevel3 + quantityImportLevel3) - quantityExportLevel3;
        tbProductBlockDetailsReportDTO.setEndProductLevel3(endProductLevel3);
        return tbProductBlockDetailsReportDTO;
    }

    private void fillReportFinishGoodsData(
        XSSFSheet sheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        List<Tuple> data) {
        int sequenceNo = 0;
        int rowIdx = 2; // after header row
        for (Tuple item : data) {
            rowIdx++;
            sequenceNo++;
            Row row = sheet.createRow(rowIdx);
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 0, sequenceNo);
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 1, "code");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 2, "productLevel1");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 3, "productLevel2");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 4, "productLevel3");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 5, "quantityImportLevel1");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 6, "quantityImportLevel2");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 7, "quantityImportLevel3");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 8, "quantityExportLevel1");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 9, "quantityExportLevel2");
            exportExcelUtil.setCellValue(borderCellStyle, item, row, 10, "quantityExportLevel3");
            TBProductBlockDetailsReportDTO tbProductBlockDetailsReportDTO = convertFinishGoods(item);
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 11, tbProductBlockDetailsReportDTO.getEndProductLevel1());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 12, tbProductBlockDetailsReportDTO.getEndProductLevel2());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 13, tbProductBlockDetailsReportDTO.getEndProductLevel3());
        }
    }

    @Override
    public void downloadReportFinishedGoodsWarehouse(Long customerId, Long cropId, Date startDate, Date endDate, HttpServletResponse response) throws IOException {
        if (endDate == null) {
            endDate = Date.from(Instant.now());
        }
        Resource resource = resourceLoader.getResource(REPORT_FINISH_GOODS_TEMPLATE_PATH);
        XSSFWorkbook workbook = new XSSFWorkbook(resource.getInputStream());
        XSSFSheet sheet = workbook.getSheetAt(0);
        if (null == sheet) return;
        CellStyle datetimeCellStyle = exportExcelUtil.createBorderCellStyle(workbook);
        CreationHelper createHelper = workbook.getCreationHelper();
        datetimeCellStyle.setDataFormat(createHelper.createDataFormat().getFormat(EXPORT_DATE_FORMAT));
        CellStyle borderCellStyle = exportExcelUtil.createBorderCellStyle(workbook);

        List<Tuple> importedFinishGoodsData = getReportFinishedGoodsWarehouseData(customerId, cropId, startDate, endDate);
        fillReportFinishGoodsData(sheet, datetimeCellStyle, borderCellStyle, importedFinishGoodsData);

        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.close();
    }

    @Override
    public Page<TBProductBlockDetailsReportDTO> reportFinishedGoodsWarehouse(Long customerId, Long cropId, Date startDate, Date endDate, Pageable pageable) {
        List<Tuple> data = getReportFinishedGoodsWarehouseData(customerId, cropId, startDate, endDate);
        List<TBProductBlockDetailsReportDTO> logDTOList = new ArrayList<>();
        for (Tuple item : data) {
            TBProductBlockDetailsReportDTO tbProductBlockDetailsReportDTO = convertFinishGoods(item);
            logDTOList.add(tbProductBlockDetailsReportDTO);
        }
        final int start = (int) pageable.getOffset();
        final int end = Math.min((start + pageable.getPageSize()), logDTOList.size());
        final Page<TBProductBlockDetailsReportDTO> page = new PageImpl<>(logDTOList.subList(start, end), pageable, logDTOList.size());
        return page;
    }

    private List<Tuple> getCropIds(Long customerId, Long cropId, Date startDate, Date endDate) {
        String sqlString = "";
        DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
        String startDateTime = dateFormatter.format(startDate) + " 00:00:00";
        String endDateTime = dateFormatter.format(endDate) + " 23:59:59";
        if (cropId != null) {
            sqlString = "SELECT tbe.id as entityId, tbe.name as entityName, tbc.id as cropId, tbc.code as cropCode\n" +
                "FROM tb_crop as tbc\n" +
                "INNER JOIN tb_entity as tbe ON(tbc.tb_entity_id=tbe.id)\n" +
                "WHERE tbc.start_date >= :startDateTime AND tbc.start_date <= :endDateTime \n" +
                "AND tbc.id= :cropId \n" +
                "AND tbe.tb_customer_id= :customerId;";
            Query query = em
                .createNativeQuery(sqlString, Tuple.class)
                .setParameter("customerId", customerId)
                .setParameter("cropId", cropId)
                .setParameter("startDateTime", startDateTime)
                .setParameter("endDateTime", endDateTime);

            return query.getResultList();
        } else {
            sqlString = "SELECT tbe.id as entityId, tbe.name as entityName, tbc.id as cropId, tbc.code as cropCode\n" +
                "FROM tb_crop as tbc\n" +
                "INNER JOIN tb_entity as tbe ON(tbc.tb_entity_id=tbe.id)\n" +
                "WHERE tbc.start_date >= :startDateTime AND tbc.start_date <= :endDateTime \n" +
                "AND tbe.tb_customer_id= :customerId;";
            Query query = em
                .createNativeQuery(sqlString, Tuple.class)
                .setParameter("customerId", customerId)
                .setParameter("startDateTime", startDateTime)
                .setParameter("endDateTime", endDateTime);

            return query.getResultList();
        }
    }

    private List<Tuple> getExeDayAndQuantity(String cropIds) {
        String parameterType = "SO_LUONG_LOAI_BO" + "SO_LUONG_HAT_GIEO" + "SO_LUONG_CAY_TRONG";
        String activityType = "ACTIVE_TYPE_NURSERY" + "ACTIVE_TYPE_PLANTING" + "ACTIVE_TYPE_HARVEST" + "ACTIVE_TYPE_PLANT_REMOVAL";
        String sqlString = "SELECT tba.tb_crop_id as cropId, tba.activity_type as activityId, tbat.name as activityName, tba.execute_date as executeDate, tbou.jhi_index as quantity\n" +
            " FROM tb_activity as tba\n" +
            " INNER JOIN tb_activity_type as tbat ON(tba.activity_type=tbat.id)\n" +
            " INNER JOIN tb_object_update as tbou ON(tbou.tb_activity_id=tba.id)\n" +
            " LEFT JOIN tb_object_parameter as tbop ON(tbou.tb_object_parameter_id=tbop.id AND tbop.name IN(:parameterType))\n" +
            " WHERE tba.tb_crop_id IN(:cropIds)\n" +
            " AND tbat.name IN(:activityType) \n" +
            " GROUP BY cropId, activityId ORDER BY cropId;";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("cropIds", cropIds)
            .setParameter("activityType", activityType)
            .setParameter("parameterType", parameterType);

        return query.getResultList();
    }

    private List<Tuple> getInOutQuantity(String cropIds) {
        String sqlString = "SELECT tbpbd.tb_crop_id as cropId,\n" +
            " SUM(CASE WHEN tbpsu.action=0 THEN tbpsu.quantity ELSE 0 END)  as inQuantity, \n" +
            " SUM(CASE WHEN tbpsu.action=1 THEN tbpsu.quantity ELSE 0 END) as outQuantity\n" +
            " FROM tb_product_block_details as tbpbd \n" +
            " INNER JOIN tb_product_stock_update as tbpsu ON(tbpsu.tb_product_block_details_id=tbpbd.id)\n" +
            " WHERE tbpbd.tb_crop_id IN(:cropIds);";

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("cropIds", cropIds);

        return query.getResultList();
    }

    private List<Tuple> getReportFarmResultDataOld(Long customerId, Long cropId, Date startDate, Date endDate) {
        String sqlString = "";
        DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
//        String currentDateTime = dateFormatter.format(startDate) + " 23:59:59";

        String startDateTime = dateFormatter.format(startDate) + " 00:00:00";
        String endDateTime = dateFormatter.format(endDate) + " 23:59:59";
        // Get all cropId have start_date between startDateTime and endDateTime

        if (cropId != null) {
            sqlString = "SELECT \n" +
                " tba.id as activityId, tbc.id as cropId, tbop.id as parameterId, tbe.name as membraneHouseName, tbc.code as lotCode,\n" +
                " CASE WHEN tbat.name='ACTIVE_TYPE_NURSERY' THEN tba.execute_date  ELSE 0 END as nurseryDay,\n" +
                " CASE WHEN tbat.name='ACTIVE_TYPE_PLANTING' THEN tba.execute_date  ELSE 0 END as plantingDay,\n" +
                " CASE WHEN tbat.name='ACTIVE_TYPE_HARVEST' THEN tba.execute_date ELSE 0 END as harvestDay,\n" +
                " CASE WHEN tbop.name='SO_LUONG_HAT_GIEO' THEN tbou.jhi_index  ELSE 0 END as nurseryNumber,\n" +
                " CASE WHEN tbop.name='SO_LUONG_CAY_TRONG' THEN tbou.jhi_index  ELSE 0 END as plantingNumber,\n" +
                " CASE WHEN tbop.name='SO_LUONG_LOAI_BO' THEN tbou.jhi_index  ELSE 0 END as plantRemovalNumber,\n" +
                " SUM(CASE WHEN tbpsu.tb_product_block_details_id=tbpbd.id AND tbpsu.action=0 THEN tbpsu.quantity ELSE 0 END)  as inQuantity, \n" +
                " SUM(CASE WHEN tbpsu.tb_product_block_details_id=tbpbd.id AND tbpsu.action=1 THEN tbpsu.quantity ELSE 0 END) as outQuantity\n" +
                " FROM tb_activity as tba\n" +
                " INNER JOIN tb_crop as tbc ON(tba.tb_crop_id=tbc.id)\n" +
                " INNER JOIN tb_entity as tbe ON(tbc.tb_entity_id=tbe.id)\n" +
                " INNER JOIN tb_activity_type as tbat ON(tba.activity_type=tbat.id)\n" +
                " INNER JOIN tb_object_update as tbou ON(tbou.tb_activity_id=tba.id)\n" +
                " INNER JOIN tb_object_parameter as tbop ON(tbou.tb_object_parameter_id=tbop.id AND tbop.name IN('SO_LUONG_LOAI_BO', 'SO_LUONG_HAT_GIEO', 'SO_LUONG_CAY_TRONG'))\n" +
                " LEFT JOIN tb_product_block_details as tbpbd ON(tbpbd.tb_crop_id=tbc.id)\n" +
                " LEFT JOIN tb_product_stock_update as tbpsu ON(tbpsu.tb_product_block_details_id=tbpbd.id)\n" +
                " WHERE tbat.name IN('ACTIVE_TYPE_NURSERY', 'ACTIVE_TYPE_PLANTING', 'ACTIVE_TYPE_HARVEST', 'ACTIVE_TYPE_PLANT_REMOVAL') AND\n" +
                " tbc.start_date >= :startUpdateDate AND tbc.start_date <= :endUpdateDate AND\n" +
                " tbe.tb_customer_id = :customerId AND tbc.id = :cropId\n" +
                " GROUP BY tbop.id, lotCode, activityId, cropId, nurseryDay, plantingDay, harvestDay, membraneHouseName;";

            Query query = em
                .createNativeQuery(sqlString, Tuple.class)
                .setParameter("customerId", customerId)
                .setParameter("cropId", cropId)
                .setParameter("startUpdateDate", startDateTime)
                .setParameter("endUpdateDate", endDateTime);

            return query.getResultList();
        } else {
            sqlString = "SELECT tba.id as activityId, tbc.id as cropId, tbop.id as parameterId, tbe.name as membraneHouseName, tbc.code as lotCode,\n" +
                " CASE WHEN tbat.name='ACTIVE_TYPE_NURSERY' THEN tba.execute_date  ELSE null END as nurseryDay,\n" +
                " CASE WHEN tbat.name='ACTIVE_TYPE_PLANTING' THEN tba.execute_date  ELSE null END as plantingDay,\n" +
                " CASE WHEN tbat.name='ACTIVE_TYPE_HARVEST' THEN tba.execute_date ELSE null END as harvestDay,\n" +
                " CASE WHEN tbop.name='SO_LUONG_HAT_GIEO' THEN tbou.jhi_index  ELSE 0 END as nurseryNumber,\n" +
                " CASE WHEN tbop.name='SO_LUONG_CAY_TRONG' THEN tbou.jhi_index  ELSE 0 END as plantingNumber,\n" +
                " CASE WHEN tbop.name='SO_LUONG_LOAI_BO' THEN tbou.jhi_index  ELSE 0 END as plantRemovalNumber,\n" +
                " SUM(CASE WHEN tbpsu.tb_product_block_details_id=tbpbd.id AND tbpsu.action=0 THEN tbpsu.quantity ELSE 0 END)  as inQuantity, \n" +
                " SUM(CASE WHEN tbpsu.tb_product_block_details_id=tbpbd.id AND tbpsu.action=1 THEN tbpsu.quantity ELSE 0 END) as outQuantity\n" +
                " FROM tb_activity as tba\n" +
                " INNER JOIN tb_crop as tbc ON(tba.tb_crop_id=tbc.id)\n" +
                " INNER JOIN tb_entity as tbe ON(tbc.tb_entity_id=tbe.id)\n" +
                " INNER JOIN tb_activity_type as tbat ON(tba.activity_type=tbat.id)\n" +
                " INNER JOIN tb_object_update as tbou ON(tbou.tb_activity_id=tba.id)\n" +
                " INNER JOIN tb_object_parameter as tbop ON(tbou.tb_object_parameter_id=tbop.id AND tbop.name IN('SO_LUONG_LOAI_BO', 'SO_LUONG_HAT_GIEO', 'SO_LUONG_CAY_TRONG'))\n" +
                " LEFT JOIN tb_product_block_details as tbpbd ON(tbpbd.tb_crop_id=tbc.id)\n" +
                " LEFT JOIN tb_product_stock_update as tbpsu ON(tbpsu.tb_product_block_details_id=tbpbd.id)\n" +
                " WHERE tbat.name IN('ACTIVE_TYPE_NURSERY', 'ACTIVE_TYPE_PLANTING', 'ACTIVE_TYPE_HARVEST', 'ACTIVE_TYPE_PLANT_REMOVAL') AND\n" +
                " tbc.start_date >= :startUpdateDate AND tbc.start_date <= :startUpdateDate AND\n" +
                " tbe.tb_customer_id = :customerId\n" +
                " GROUP BY tbop.id, lotCode, activityId, cropId, nurseryDay, plantingDay, harvestDay, membraneHouseName;";

            Query query = em
                .createNativeQuery(sqlString, Tuple.class)
                .setParameter("customerId", customerId)
                .setParameter("startUpdateDate", startDateTime)
                .setParameter("endUpdateDate", endDateTime);

            return query.getResultList();
        }
    }

    private TBCropForReportListDTO getCropInfo(List<Tuple> crops) {
        TBCropForReportListDTO result = new TBCropForReportListDTO();
        List<TBCropForReportDTO> list = new ArrayList<>();
        String cropIds = "";
        for (Tuple data : crops) {
            TBCropForReportDTO tbCropForReportDTO = new TBCropForReportDTO();
            Long entityId = ((BigInteger) data.get("entityId")).longValue();
            Long cropId = ((BigInteger) data.get("cropId")).longValue();
            if (cropIds == "") {
                cropIds = String.valueOf(cropId);
            } else {
                cropIds = cropIds + "," + cropId;
            }
            String entityName = String.valueOf(data.get("entityName"));
            String cropCode = String.valueOf(data.get("cropCode"));
            tbCropForReportDTO.setCropCode(cropCode);
            tbCropForReportDTO.setCropId(cropId);
            tbCropForReportDTO.setEntityId(entityId);
            tbCropForReportDTO.setEntityName(entityName);
            list.add(tbCropForReportDTO);
        }
        result.setCropIds(cropIds);
        return result;
    }

    private TBExeDayAndQuantityListDTO getExeDayAndQuantityInfo(List<Tuple> getExeDayAndQuantities) {
        TBExeDayAndQuantityListDTO result = new TBExeDayAndQuantityListDTO();
        List<TBExeDayAndQuantityDTO> list = new ArrayList<>();
        String cropIds = "";
        for (Tuple data : getExeDayAndQuantities) {
            TBExeDayAndQuantityDTO tbExeDayAndQuantityDTO = new TBExeDayAndQuantityDTO();
            Long activityId = ((BigInteger) data.get("activityId")).longValue();
            String activityName = String.valueOf(data.get("activityName"));
            Long cropId = ((BigInteger) data.get("cropId")).longValue();
            if (cropIds == "") {
                cropIds = String.valueOf(cropId);
            } else {
                cropIds = cropIds + "," + cropId;
            }
            String executeDateStr = String.valueOf(data.get("executeDate"));
            java.sql.Date executeDate = (java.sql.Date) data.get(executeDateStr);
            String quantity = String.valueOf(data.get("quantity"));
            Double quantityFromString = 0.0;
            try {
                quantityFromString = Double.valueOf(quantity);
            } catch (Exception exception) {
                quantityFromString = 0.0;
            }
            tbExeDayAndQuantityDTO.setCropId(cropId);
            tbExeDayAndQuantityDTO.setActivityId(activityId);
            tbExeDayAndQuantityDTO.setActivityName(activityName);
            tbExeDayAndQuantityDTO.setQuantity(quantityFromString);
            tbExeDayAndQuantityDTO.setExecuteDate(executeDate);
            list.add(tbExeDayAndQuantityDTO);
        }
        result.setCropIds(cropIds);
        return result;
    }

    private List<TBInOutQuantityDTO> getInOutInfo(List<Tuple> inOutInfo) {
        List<TBInOutQuantityDTO> list = new ArrayList<>();

        for (Tuple data : inOutInfo) {
            TBInOutQuantityDTO tbInOutQuantityDTO = new TBInOutQuantityDTO();
            Long cropId = ((BigInteger) data.get("cropId")).longValue();
            Double inQuantity = Double.valueOf((Double) data.get("inQuantity"));
            Double outQuantity = Double.valueOf((Double) data.get("outQuantity"));
            tbInOutQuantityDTO.setCropId(cropId);
            tbInOutQuantityDTO.setOutQuantity(outQuantity);
            tbInOutQuantityDTO.setInQuantity(inQuantity);
            list.add(tbInOutQuantityDTO);
        }
        return list;
    }

    private List<TBFarmResultReportDTO> getReportFarmResultData(Long customerId, Long cropId, Date startDate, Date endDate) {
        List<Tuple> getCropIds = getCropIds(customerId, cropId, startDate, endDate);
        TBCropForReportListDTO cropInfo = getCropInfo(getCropIds);
        String cropIds = cropInfo.getCropIds();
        List<Tuple> getExeDayAndQuantities = getExeDayAndQuantity(cropIds);

        TBExeDayAndQuantityListDTO exeDayAndQuantityInfo = getExeDayAndQuantityInfo(getExeDayAndQuantities);
        List<TBExeDayAndQuantityDTO> tbExeDayAndQuantityDTOList = exeDayAndQuantityInfo.getTbExeDayAndQuantityDTOList();
        String cropIds_1 = exeDayAndQuantityInfo.getCropIds();
        List<Tuple> inOutInfo = getInOutQuantity(cropIds_1);
        List<TBInOutQuantityDTO> inOutQuantityDTOList = getInOutInfo(inOutInfo);

        List<TBFarmResultReportDTO> result = new ArrayList<>();
        List<TBCropForReportDTO> tbCropForReportDTOList = cropInfo.getTbCropForReportDTOList();
        for (TBCropForReportDTO item : tbCropForReportDTOList) {
            TBFarmResultReportDTO tbFarmResultReportDTO = new TBFarmResultReportDTO();
            tbFarmResultReportDTO.setLotCode(item.getCropCode());
            tbFarmResultReportDTO.setMembraneHouseName(item.getEntityName());
            for (TBExeDayAndQuantityDTO item2 : tbExeDayAndQuantityDTOList) {
                if (item2.getActivityName().equals("ACTIVE_TYPE_NURSERY") && item2.getCropId().equals(item.getCropId())) {
                    tbFarmResultReportDTO.setNurseryNumber(item2.getQuantity());
                    tbFarmResultReportDTO.setNurseryDay(item2.getExecuteDate());
                }
                if (item2.getActivityName().equals("ACTIVE_TYPE_PLANTING") && item2.getCropId().equals(item.getCropId())) {
                    tbFarmResultReportDTO.setPlantingNumber(item2.getQuantity());
                    tbFarmResultReportDTO.setPlantingDay(item2.getExecuteDate());
                }
                if (item2.getActivityName().equals("ACTIVE_TYPE_HARVEST") && item2.getCropId().equals(item.getCropId())) {
                    tbFarmResultReportDTO.setHarvestDay(item2.getExecuteDate());
                }
                if (item2.getActivityName().equals("ACTIVE_TYPE_PLANT_REMOVAL") && item2.getCropId().equals(item.getCropId())) {
                    tbFarmResultReportDTO.setPlantRemovalNumber(item2.getQuantity());
                }
            }
            for (TBInOutQuantityDTO item3 : inOutQuantityDTOList) {
                if (item.getCropId().equals(item3.getCropId())) {
                    tbFarmResultReportDTO.setOutQuantity(item3.getOutQuantity());
                    tbFarmResultReportDTO.setInQuantity(item3.getInQuantity());
                }
            }

            result.add(tbFarmResultReportDTO);
        }
        return result;
    }

    public TBFarmResultReportDTO convertFarmResult(Tuple data) {
        TBFarmResultReportDTO tbFarmResultReportDTO = new TBFarmResultReportDTO();
        Long activityId = ((BigInteger) data.get("activityId")).longValue();
        Long cropId = ((BigInteger) data.get("cropId")).longValue();
        Long parameterId = ((BigInteger) data.get("parameterId")).longValue();
        String membraneHouseName = String.valueOf(data.get("membraneHouseName"));
        String lotCode = String.valueOf(data.get("lotCode"));
        Double nurseryNumber = Double.valueOf((Double) data.get("nurseryNumber"));
        Double plantingNumber = Double.valueOf((Double) data.get("plantingNumber"));
        Double plantRemovalNumber = Double.valueOf((Double) data.get("plantRemovalNumber"));
        Double inQuantity = Double.valueOf((Double) data.get("inQuantity"));
        Double outQuantity = Double.valueOf((Double) data.get("outQuantity"));
        Float passRate = Float.valueOf(0);
        if (plantingNumber > 0) {
            passRate = Float.valueOf((float) (outQuantity / plantingNumber));
        }
        Integer numberDaysCultivation = 0;
        java.sql.Date nurseryDay = (java.sql.Date) data.get("nurseryDay");
        java.sql.Date plantingDay = (java.sql.Date) data.get("plantingDay");
        java.sql.Date harvestDay = (java.sql.Date) data.get("harvestDay");

        if (harvestDay != null) {

        }

        tbFarmResultReportDTO.setActivityId(activityId);
        tbFarmResultReportDTO.setCropId(cropId);
        tbFarmResultReportDTO.setParameterId(parameterId);
        tbFarmResultReportDTO.setMembraneHouseName(membraneHouseName);
        tbFarmResultReportDTO.setLotCode(lotCode);
        tbFarmResultReportDTO.setNurseryNumber(nurseryNumber);
        tbFarmResultReportDTO.setPlantingNumber(plantingNumber);
        tbFarmResultReportDTO.setPlantRemovalNumber(plantRemovalNumber);
        tbFarmResultReportDTO.setInQuantity(inQuantity);
        tbFarmResultReportDTO.setOutQuantity(outQuantity);
//        tbFarmResultReportDTO.setPassRate(passRate);
        tbFarmResultReportDTO.setNumberDaysCultivation(numberDaysCultivation);
        return tbFarmResultReportDTO;
    }

    private void fillReportFarmResult(
        XSSFSheet sheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        List<TBFarmResultReportDTO> data) {
        int sequenceNo = 0;
        int rowIdx = 2; // after header row
        for (TBFarmResultReportDTO item : data) {
            rowIdx++;
            sequenceNo++;
            Row row = sheet.createRow(rowIdx);
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 0, sequenceNo);
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 1, item.getMembraneHouseName());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 2, item.getLotCode());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 3, item.getNurseryDay());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 4, item.getPlantingDay());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 5, item.getNurseryNumber());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 6, item.getPlantRemovalNumber());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 7, item.getHarvestDay());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 8, item.getInQuantity());
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 9, item.getOutQuantity());
            Double passRate = 0.0;
            if (item.getPlantingNumber() > 0) {
                passRate = item.getOutQuantity() / item.getPlantingNumber();
            }
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 10, passRate);
            Integer numberDaysCultivation = 0;
            if (item.getNurseryDay() != null && item.getHarvestDay() != null) {
                Period period = Period.between(item.getHarvestDay().toLocalDate(), item.getNurseryDay().toLocalDate());
                numberDaysCultivation = Math.abs(period.getDays());
            } else {
                if (item.getPlantingDay() != null && item.getHarvestDay() != null) {
                    Period period = Period.between(item.getHarvestDay().toLocalDate(), item.getPlantingDay().toLocalDate());
                    numberDaysCultivation = Math.abs(period.getDays());
                }
            }
            exportExcelUtil.setSequenceNoCellValue(borderCellStyle, row, 11, numberDaysCultivation);
        }
    }

    @Override
    public void downloadReportFarmResult(Long customerId, Long cropId, Date startDate, Date endDate, HttpServletResponse response) throws IOException {
        if (endDate == null) {
            endDate = Date.from(Instant.now());
        }
        Resource resource = resourceLoader.getResource(REPORT_FARM_RESULT_TEMPLATE_PATH);
        XSSFWorkbook workbook = new XSSFWorkbook(resource.getInputStream());
        XSSFSheet sheet = workbook.getSheetAt(0);
        if (null == sheet) return;
        CellStyle datetimeCellStyle = exportExcelUtil.createBorderCellStyle(workbook);
        CreationHelper createHelper = workbook.getCreationHelper();
        datetimeCellStyle.setDataFormat(createHelper.createDataFormat().getFormat(EXPORT_DATE_FORMAT));
        CellStyle borderCellStyle = exportExcelUtil.createBorderCellStyle(workbook);

        List<TBFarmResultReportDTO> importedFarmResultData = getReportFarmResultData(customerId, cropId, startDate, endDate);
        fillReportFarmResult(sheet, datetimeCellStyle, borderCellStyle, importedFarmResultData);

        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.close();
    }

    @Override
    public Page<TBFarmResultReportDTO> reportFarmResult(Long customerId, Long cropId, Date startDate, Date endDate, Pageable pageable) {
        List<TBFarmResultReportDTO> data = getReportFarmResultData(customerId, cropId, startDate, endDate);
        List<TBFarmResultReportDTO> result = new ArrayList<>();
        for (TBFarmResultReportDTO item : data) {
            Double passRate = 0.0;
            if (item.getPlantingNumber() > 0) {
                passRate = item.getOutQuantity() / item.getPlantingNumber();
            }
            Integer numberDaysCultivation = 0;
            if (item.getNurseryDay() != null && item.getHarvestDay() != null) {
                Period period = Period.between(item.getHarvestDay().toLocalDate(), item.getNurseryDay().toLocalDate());
                numberDaysCultivation = Math.abs(period.getDays());
            } else {
                if (item.getPlantingDay() != null && item.getHarvestDay() != null) {
                    Period period = Period.between(item.getHarvestDay().toLocalDate(), item.getPlantingDay().toLocalDate());
                    numberDaysCultivation = Math.abs(period.getDays());
                }
            }
            item.setNumberDaysCultivation(numberDaysCultivation);
            item.setPassRate(passRate);
            result.add(item);
        }
        final int start = (int) pageable.getOffset();
        final int end = Math.min((start + pageable.getPageSize()), result.size());
        final Page<TBFarmResultReportDTO> page = new PageImpl<>(result.subList(start, end), pageable, result.size());
        return page;
    }

}
