package vn.azteam.tpf.service.impl;

import com.microsoft.schemas.office.word.STWrapType;
import com.microsoft.schemas.vml.CTGroup;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.Units;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
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.PageRequest;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vn.azteam.tpf.Constants.Constants;
import vn.azteam.tpf.service.ReportsService;
import vn.azteam.tpf.service.dto.ActivityInMonthDTO;
import vn.azteam.tpf.service.dto.SeedUsageReportDTO;
import vn.azteam.tpf.domain.*;
import vn.azteam.tpf.repository.TBActivityRepository;
import vn.azteam.tpf.service.*;
import vn.azteam.tpf.service.dto.*;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.data.domain.Pageable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;

import java.util.*;

/**
 * Service Implementation for managing Reports.
 */
@Service
@Transactional
public class ReportsServiceImpl implements ReportsService {

    private final Logger log = LoggerFactory.getLogger(ReportsServiceImpl.class);
    private final Integer GAP_BM01_SHEET_IDX = 0;
    private final Integer GAP_BM02_SHEET_IDX = 1;
    private final Integer GAP_BM03_SHEET_IDX = 2;
    private final Integer GAP_BM04_SHEET_IDX = 3;
    private final Integer GAP_BM05_SHEET_IDX = 4;
    private final Integer GAP_BM06_SHEET_IDX = 5;
    private final Integer GAP_BM07_SHEET_IDX = 6;
    private final Integer GAP_BM08_SHEET_IDX = 7;
    private final Integer GAP_BM10_SHEET_IDX = 9;
    private final Integer GAP_BM11_SHEET_IDX = 10;
    private final Integer GAP_BM12_SHEET_IDX = 11;

    private final String EXPORT_DATE_FORMAT = "dd/MM/yyyy";
    private final String GARDEN_SUPERVISION_TEMPLATE_PATH = "classpath:templates/report/gardenSupervisionTemplate.xlsx";
    private final String IMPORT_EXPORT_WAREHOUSE_TEMPLATE_PATH = "classpath:templates/report/importExportWarehouse.xlsx";
    private final String IMPORT_EXPORT_SUPPLIES_IN_WAREHOUSE_TEMPLATE_PATH = "classpath:templates/report/importExportSuppliesInWarehouse.xlsx";
    private final String IMPORT_MATERIAL_TEMPLATE_PATH = "classpath:templates/report/importMaterialTracking.xlsx";
    private final String NURSERY_MONITORING_TEMPLATE_PATH = "classpath:templates/msword/BieuMauTheoDoiNhaUom.docx";
    private final String CULTIVATE_HISTORY_TEMPLATE_PATH = "classpath:templates/msword/BieuMauNhatKyCanhTacTaiKhuCanhTac.docx";

    private final String FONT_DEFAULT_EXPORT = "Times New Roman";

    private final EntityManager em;
    private final TBActivityService tbActivityService;
    private final TBObjectUpdateService tbObjectUpdateService;
    private final TBEnvironmentUpdateService tbEnvironmentUpdateService;
    private final TBEnvironmentalParameterService tbEnvironmentalParameterService;
    private final TBObjectParameterService tbObjectParameterService;
    private final UserService userService;
    private final ResourceLoader resourceLoader;

    public ReportsServiceImpl(EntityManager em,
                          TBActivityService tbActivityService,
                          TBObjectUpdateService tbObjectUpdateService,
                          TBEnvironmentUpdateService tbEnvironmentUpdateService,
                          TBEnvironmentalParameterService tbEnvironmentalParameterService,
                          TBObjectParameterService tbObjectParameterService,
                          UserService userService,
                          ResourceLoader resourceLoader) {
        this.em = em;
        this.tbActivityService = tbActivityService;
        this.tbObjectUpdateService = tbObjectUpdateService;
        this.tbEnvironmentUpdateService = tbEnvironmentUpdateService;
        this.tbEnvironmentalParameterService = tbEnvironmentalParameterService;
        this.tbObjectParameterService = tbObjectParameterService;
        this.userService = userService;
        this.resourceLoader = resourceLoader;
    }

    @Override
    @Transactional(readOnly = true)
    public void exportGardenSupervisionToExcel(
        Long customerId,
        Date startDate,
        Date endDate,
        HttpServletResponse response) throws IOException {

        if (endDate == null) {
            endDate = Date.from(Instant.now());
        }

        Resource resource = resourceLoader.getResource(GARDEN_SUPERVISION_TEMPLATE_PATH);
        XSSFWorkbook workbook = new XSSFWorkbook(resource.getInputStream());
        XSSFSheet plantingSheet = workbook.getSheetAt(GAP_BM01_SHEET_IDX);
        XSSFSheet sprayingSheet = workbook.getSheetAt(GAP_BM02_SHEET_IDX);
        XSSFSheet harvestSheet = workbook.getSheetAt(GAP_BM03_SHEET_IDX);
        XSSFSheet manuringUsagesSheet = workbook.getSheetAt(GAP_BM04_SHEET_IDX);
        XSSFSheet sprayingUsagesSheet = workbook.getSheetAt(GAP_BM05_SHEET_IDX);
        XSSFSheet seedListingSheet = workbook.getSheetAt(GAP_BM06_SHEET_IDX);
        XSSFSheet substrateListingSheet = workbook.getSheetAt(GAP_BM07_SHEET_IDX);
        XSSFSheet substrateUsagesHistorySheet = workbook.getSheetAt(GAP_BM08_SHEET_IDX);
        XSSFSheet activitySheet = workbook.getSheetAt(GAP_BM10_SHEET_IDX);
        XSSFSheet plantHealthSheet = workbook.getSheetAt(GAP_BM11_SHEET_IDX);
        XSSFSheet waterUsageSheet = workbook.getSheetAt(GAP_BM12_SHEET_IDX);

        if (plantingSheet == null ||
            sprayingSheet == null ||
            harvestSheet == null ||
            manuringUsagesSheet == null ||
            sprayingUsagesSheet == null ||
            seedListingSheet == null ||
            substrateListingSheet == null ||
            substrateUsagesHistorySheet == null ||
            activitySheet == null ||
            plantHealthSheet == null ||
            waterUsageSheet == null)
            return;

        CellStyle datetimeCellStyle = createBorderCellStyle(workbook);
        CreationHelper createHelper = workbook.getCreationHelper();
        datetimeCellStyle.setDataFormat(
            createHelper.createDataFormat().getFormat(EXPORT_DATE_FORMAT));
        CellStyle borderCellStyle = createBorderCellStyle(workbook);

        CellStyle wrapTextCellStyle = createBorderCellStyle(workbook);
        wrapTextCellStyle.setWrapText(true);

        CellStyle centerCellStyle = createBorderCellStyle(workbook);
        centerCellStyle.setAlignment(HorizontalAlignment.CENTER);

        List<Tuple> plantingData = getPlantingData(customerId, startDate, endDate);
        List<Tuple> sprayingHistoryData = getSprayingHistoryData(customerId, startDate, endDate);
        List<Tuple> harvestData = getHarvestData(customerId, startDate, endDate);
        List<Tuple> manuringUsageListingData = getManuringUsageListingData(customerId, startDate, endDate);
        List<Tuple> sprayingUsageListingData = getSprayingUsageListingData(customerId, startDate, endDate);
        List<Tuple> seedUsageListingData = getSeedUsageListingData(customerId, startDate, endDate);
        List<Tuple> substrateUsageListingData = getSubstrateUsageListingData(customerId, startDate, endDate);
        List<Tuple> substrateUsageHistoryData = getSubstrateUsageHistoryData(customerId, startDate, endDate);
        List<Tuple> activityHistoryData = getActivityHistoryData(customerId, startDate, endDate);
        List<Tuple> plantHeathAndPestStatusData = getPlantHeathAndPestStatusData(customerId, startDate, endDate);
        List<Tuple> waterUsageData = getWaterUsageData(customerId, startDate, endDate);

        fillPlantingData(plantingSheet, datetimeCellStyle, borderCellStyle, plantingData);
        fillSprayingData(sprayingSheet, datetimeCellStyle, borderCellStyle, sprayingHistoryData);
        fillHarvestData(harvestSheet, datetimeCellStyle, borderCellStyle, harvestData);
        fillManuringUsageListingData(manuringUsagesSheet, borderCellStyle, manuringUsageListingData);
        fillSprayingUsageListingData(sprayingUsagesSheet, borderCellStyle, sprayingUsageListingData);
        fillSeedUsageListingData(seedListingSheet, borderCellStyle, seedUsageListingData);
        fillSubstrateUsageListingData(substrateListingSheet, borderCellStyle, substrateUsageListingData);
        fillSubstrateUsageHistoryData(substrateUsagesHistorySheet, datetimeCellStyle, borderCellStyle, substrateUsageHistoryData);
        fillPlantHeathAndPestStatusData(plantHealthSheet, datetimeCellStyle, borderCellStyle, wrapTextCellStyle, plantHeathAndPestStatusData);
        fillWaterUsageData(waterUsageSheet, datetimeCellStyle, borderCellStyle, centerCellStyle, waterUsageData);

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

    private CellStyle createBorderCellStyle(XSSFWorkbook workbook) {
        CellStyle borderCellStyle = workbook.createCellStyle();
        borderCellStyle.setBorderTop(BorderStyle.THIN);
        borderCellStyle.setBorderBottom(BorderStyle.THIN);
        borderCellStyle.setBorderLeft(BorderStyle.THIN);
        borderCellStyle.setBorderRight(BorderStyle.THIN);
        return borderCellStyle;
    }

    @Override
    public List<ActivityInMonthDTO> getMonthlyActivity(Integer month, Integer year, Long customerId, Long cropId) {
        List<Tuple> monthlyActivities = getMonthlyActivityData(month, year, customerId, cropId);
        List<ActivityInMonthDTO> lstActivityInMonthDTO = new ArrayList<>();
        for (Tuple monthlyActivity : monthlyActivities) {
            ActivityInMonthDTO activityInMonthDTO = new ActivityInMonthDTO();
            activityInMonthDTO.setDate(String.valueOf(monthlyActivity.get("activityDate")));
            activityInMonthDTO.setCountActivity(
                Long.valueOf(((BigInteger) monthlyActivity.get("activityCount")).longValue()));
            lstActivityInMonthDTO.add(activityInMonthDTO);
        }
        return lstActivityInMonthDTO;
    }

    @Override
    public List<SeedUsageReportDTO> getSeedUsageReport(Long customerId) {
        log.debug("find seed usage report");

        List<SeedUsageReportDTO> seedUsageReportDTOS = new ArrayList<>();
        List<Tuple> seedUsages = getSeedUsagesData(customerId);

        Double totalSeedUsage =
            seedUsages
                .stream()
                .filter(x -> x.get("seedQuantityUsage") != null)
                .mapToDouble(x -> Double.valueOf((Double) x.get("seedQuantityUsage"))).sum();

        for (Tuple item : seedUsages) {
            SeedUsageReportDTO dto = new SeedUsageReportDTO();
            Long seedId = Long.valueOf(((BigInteger) item.get("seedId")).longValue());
            String seedSku = String.valueOf(item.get("seedSku"));
            String seedName = String.valueOf(item.get("seedName"));
            Double seedQuantityUsage =
                item.get("seedQuantityUsage") == null ? 0 : Double.valueOf((Double) item.get("seedQuantityUsage"));
            dto.setSeedId(seedId);
            dto.setSeedSku(seedSku);
            dto.setSeedName(seedName);
            double usagePercentage = 0;
            if (totalSeedUsage > 0) {
                usagePercentage = Math.round(((double) seedQuantityUsage / totalSeedUsage) * 1000.0) / 10.0;
            }
            dto.setUsagePercentage(usagePercentage);
            seedUsageReportDTOS.add(dto);
        }
        return seedUsageReportDTOS;
    }

    @Override
    public void downloadImportExportWarehouseReport(
        Long customerId,
        Date startDate,
        Date endDate,
        HttpServletResponse response) throws IOException {
        if (endDate == null) {
            endDate = Date.from(Instant.now());
        }
        Resource resource = resourceLoader.getResource(IMPORT_EXPORT_WAREHOUSE_TEMPLATE_PATH);
        XSSFWorkbook workbook = new XSSFWorkbook(resource.getInputStream());
        XSSFSheet sheet = workbook.getSheetAt(0);
        if (sheet == null)
            return;

        CellStyle datetimeCellStyle = createBorderCellStyle(workbook);
        CreationHelper createHelper = workbook.getCreationHelper();
        datetimeCellStyle.setDataFormat(
            createHelper.createDataFormat().getFormat(EXPORT_DATE_FORMAT));
        CellStyle borderCellStyle = createBorderCellStyle(workbook);

        List<Tuple> importExportWarehouseData = getImportExportWarehouseData(customerId, startDate, endDate);
        fillImportExportWarehouseData(sheet, datetimeCellStyle, borderCellStyle, importExportWarehouseData);

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

    @Override
    public void downloadImportExportSuppliesInWarehouseReport(
        ReportImportExportSuppliesInWareHouseCriteria criteria,
        HttpServletResponse response) throws IOException {
        if (criteria.getEndDate() == null) {
            criteria.setEndDate(Date.from(Instant.now()));
        }
        Resource resource = resourceLoader.getResource(IMPORT_EXPORT_SUPPLIES_IN_WAREHOUSE_TEMPLATE_PATH);
        XSSFWorkbook workbook = new XSSFWorkbook(resource.getInputStream());
        XSSFSheet sheet = workbook.getSheetAt(0);
        if (sheet == null)
            return;

        CellStyle datetimeCellStyle = createBorderCellStyle(workbook);
        CreationHelper createHelper = workbook.getCreationHelper();
        datetimeCellStyle.setDataFormat(
            createHelper.createDataFormat().getFormat(EXPORT_DATE_FORMAT));
        CellStyle borderCellStyle = createBorderCellStyle(workbook);

        List<Tuple> tuples = getImportExportSuppliesInWarehouseData(criteria);
        fillImportExportSuppliesWarehouseData(sheet, datetimeCellStyle, borderCellStyle, tuples);

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

    @Override
    public void downloadImportMaterialReport(Long customerId, Date startDate, Date endDate, HttpServletResponse response) throws IOException {
        if (endDate == null) {
            endDate = Date.from(Instant.now());
        }
        Resource resource = resourceLoader.getResource(IMPORT_MATERIAL_TEMPLATE_PATH);
        XSSFWorkbook workbook = new XSSFWorkbook(resource.getInputStream());
        XSSFSheet sheet = workbook.getSheetAt(0);
        if (null == sheet) return;

        CellStyle datetimeCellStyle = createBorderCellStyle(workbook);
        CreationHelper createHelper = workbook.getCreationHelper();
        datetimeCellStyle.setDataFormat(
            createHelper.createDataFormat().getFormat(EXPORT_DATE_FORMAT));
        CellStyle borderCellStyle = createBorderCellStyle(workbook);

        List<Tuple> importedMaterialData = getImportedMaterialData(customerId, startDate, endDate);
        fillImportedMaterialData(sheet, datetimeCellStyle, borderCellStyle, importedMaterialData);

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

    private void fillImportExportWarehouseData(
        XSSFSheet sheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        List<Tuple> data) {
        int rowIdx = 3; // after header row
        for (Tuple item : data) {
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setCellValue(datetimeCellStyle, item, row, 0, "executeDate");
            setCellValue(borderCellStyle, item, row, 1, "cropCode");
            setCellValue(borderCellStyle, item, row, 2, "harvestQuantityLv1");
            setCellValue(borderCellStyle, item, row, 3, "harvestQuantityLv2");
            setCellValue(borderCellStyle, item, row, 4, "harvestuantityLv3");
            setCellValue(borderCellStyle, item, row, 5, "sellQuantityLv1");
            setCellValue(borderCellStyle, item, row, 6, "sellQuantityLv2");
            setCellValue(borderCellStyle, item, row, 7, "sellQuantityLv3");
            setCellValue(borderCellStyle, item, row, 8, "buyer");
            setCellValue(borderCellStyle, item, row, 9, "executeBy");
        }
    }

    private void fillImportExportSuppliesWarehouseData(
        XSSFSheet sheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        List<Tuple> data) {
        int rowIdx = 2; // after header row
        int sequenceNo = 0;
        for (Tuple item : data) {
            rowIdx++;
            sequenceNo++;
            Row row = sheet.createRow(rowIdx);
            setSequenceNoCellValue(borderCellStyle, row, 0, sequenceNo);
            setCellValue(datetimeCellStyle, item, row, 1, "supplyTypeDescription");
            setCellValue(borderCellStyle, item, row, 2, "blockCode");
            // setCellValue(borderCellStyle, item, row, 3, "updateDate");
            setCellValue(borderCellStyle, item, row, 3, "beginningStock");
            setCellValue(borderCellStyle, item, row, 4, "quantityIncomingStock");
            setCellValue(borderCellStyle, item, row, 5, "quantityOutgoingStock");
            setCellValue(borderCellStyle, item, row, 6, "endingStock");
            setStringCellValue(borderCellStyle, row, 7, "");
            setStringCellValue(borderCellStyle, row, 8, "");
            setStringCellValue(borderCellStyle, row, 9, "");
        }
    }

    private void fillImportedMaterialData(
        XSSFSheet sheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        List<Tuple> data) {
        int rowIdx = 2; // after header row
        for (Tuple item : data) {
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setCellValue(datetimeCellStyle, item, row, 0, "stockUpdateDate");
            setCellValue(borderCellStyle, item, row, 1, "suppliesTypeDescription");
            setCellValue(borderCellStyle, item, row, 2, "supplyName");
            setCellValue(borderCellStyle, item, row, 3, "supplyUnit");
            setCellValue(borderCellStyle, item, row, 4, "stockUpdateQuantity");
            setCellValue(borderCellStyle, item, row, 5, "vendorName");
            setCellValue(borderCellStyle, item, row, 6, "executeBy");
            setCellValue(borderCellStyle, item, row, 7, "stockUpdateDescription");
        }
    }

    private void fillWaterUsageData(
        XSSFSheet sheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        CellStyle centerAlignCellStyle,
        List<Tuple> data) {
        int rowIdx = 2; // after header row
        for (Tuple item : data) {
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setCellValue(datetimeCellStyle, item, row, 0, "executeDate");
            setCellValue(borderCellStyle, item, row, 1, "waterAmount");
            setCellValue(centerAlignCellStyle, item, row, 2, "isPlantsWater", true);
            setCellValue(centerAlignCellStyle, item, row, 3, "isDomesticWater", true);
            setCellValue(centerAlignCellStyle, item, row, 4, "isOtherWater", true);
            setCellValue(borderCellStyle, item, row, 5, "executeBy");
        }
    }

    private void fillPlantHeathAndPestStatusData(
        XSSFSheet sheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        CellStyle wrapTextCellStyle,
        List<Tuple> tupleList) {
        int rowIdx = 3; // after header row
        for (Tuple item : tupleList) {
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setCellValue(datetimeCellStyle, item, row, 0, "executeDate");
            setCellValue(borderCellStyle, item, row, 1, "cropCode");
            setCellValue(wrapTextCellStyle, item, row, 2, "cropStatus");

            if (item.get("pestStatus") != null) {
                String consolidatePestStatus = String.valueOf(item.get("pestStatus"));
                String[] pestStatusKeyPairs = consolidatePestStatus.split("\\|");
                for (String pestStatusKeyPair : pestStatusKeyPairs) {
                    String[] pestStatus = pestStatusKeyPair.split("@@");
                    if (pestStatus.length < 2) {
                        continue;
                    }
                    String pestStatusKey = pestStatus[0];
//                    if (LOAI_DICH_HAI.name().equals(pestStatusKey)) {
//                        setStringCellValue(borderCellStyle, row, 3, pestStatus[1]);
//                    } else if (MUC_DO_GAY_HAI.name().equals(pestStatusKey)) {
//                        setStringCellValue(borderCellStyle, row, 4, pestStatus[1]);
//                    } else if (THIEN_DICH.name().equals(pestStatusKey)) {
//                        setStringCellValue(borderCellStyle, row, 5, pestStatus[1]);
//                    } else if (BIEN_PHAP_XU_LY.name().equals(pestStatusKey)) {
//                        setStringCellValue(borderCellStyle, row, 6, pestStatus[1]);
//                    }
                }
            } else {
                setStringCellValue(borderCellStyle, row, 3, "");
                setStringCellValue(borderCellStyle, row, 4, "");
                setStringCellValue(borderCellStyle, row, 5, "");
                setStringCellValue(borderCellStyle, row, 6, "");
            }
            setCellValue(borderCellStyle, item, row, 7, "executeBy");
            setCellValue(borderCellStyle, item, row, 8, "description");
        }
    }

    private void fillActivityHistoryData(
        XSSFSheet sheet,
        XSSFSheet activityByCropSheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        CellStyle centerAlignCellStyle,
        List<Tuple> data) {
        int rowIdx = 2; // after header row
        for (Tuple item : data) {
            rowIdx++;
            setActivitySheetData(sheet, datetimeCellStyle, borderCellStyle, centerAlignCellStyle, rowIdx, item, "netHouseName");
            setActivitySheetData(activityByCropSheet, datetimeCellStyle, borderCellStyle, centerAlignCellStyle, rowIdx, item, "cropCode");
        }
    }

    private void setActivitySheetData(
        XSSFSheet activityByCropSheet,
        CellStyle datetimeCellStyle,
        CellStyle borderCellStyle,
        CellStyle centerAlignCellStyle,
        int rowIdx,
        Tuple item,
        String netHouseNameOrCropCode) {
        activityByCropSheet.copyRows(rowIdx, rowIdx, rowIdx + 1, new CellCopyPolicy());
        Row row = activityByCropSheet.getRow(rowIdx);
        setCellValue(datetimeCellStyle, item, row, 0, "executeDate");
        setCellValue(borderCellStyle, item, row, 1, netHouseNameOrCropCode);
        setCellValue(centerAlignCellStyle, item, row, 2, "isCleanGarden", true);
        setCellValue(centerAlignCellStyle, item, row, 3, "isPrepareGarden", true);
        setCellValue(centerAlignCellStyle, item, row, 4, "isPlanting", true);
        setCellValue(centerAlignCellStyle, item, row, 5, "isHangVines", true);
        setCellValue(centerAlignCellStyle, item, row, 6, "isPollination", true);
        setCellValue(centerAlignCellStyle, item, row, 7, "isSpraying", true);
        setCellValue(centerAlignCellStyle, item, row, 8, "isHarvest", true);
        setCellValue(centerAlignCellStyle, item, row, 9, "isCleanHarvestTools", true);
        setCellValue(centerAlignCellStyle, item, row, 10, "isCleanHandBeforeHarvest", true);
        setCellValue(centerAlignCellStyle, item, row, 11, "isOtherActivity", true);
        setCellValue(borderCellStyle, item, row, 12, "executeBy");
        setCellValue(borderCellStyle, item, row, 13, "description");
    }

    private void fillSubstrateUsageHistoryData(XSSFSheet sheet, CellStyle datetimeCellStyle, CellStyle borderCellStyle, List<Tuple> data) {
        int rowIdx = 2; // after header row
        for (Tuple substrate : data) {
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setCellValue(datetimeCellStyle, substrate, row, 0, "nurseryExecuteDate");
            setCellValue(borderCellStyle, substrate, row, 1, "substrateType");
            setCellValue(borderCellStyle, substrate, row, 2, "substrateCropCode");
            setCellValue(borderCellStyle, substrate, row, 3, "quantity");
            setCellValue(borderCellStyle, substrate, row, 4, "plantingCropCode");
            setCellValue(borderCellStyle, substrate, row, 5, "executeBy");
        }
    }

    private void fillSubstrateUsageListingData(XSSFSheet sheet, CellStyle borderCellStyle, List<Tuple> data) {
        int rowIdx = 2; // after header row
        int sequenceNo = 0;
        for (Tuple substrate : data) {
            sequenceNo++;
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setSequenceNoCellValue(borderCellStyle, row, 0, sequenceNo);
            //setCellValue(borderCellStyle, substrate, row, 1, "substrateType");
            setCellValue(borderCellStyle, substrate, row, 2, "substrateName");
            setCellValue(borderCellStyle, substrate, row, 3, "substrateManufacturer");
            setCellValue(borderCellStyle, substrate, row, 4, "vendorName");
        }
    }

    private void fillSeedUsageListingData(XSSFSheet sheet, CellStyle borderCellStyle, List<Tuple> seedUsagesListing) {
        int rowIdx = 2; // after header row
        int sequenceNo = 0;
        for (Tuple seedUsage : seedUsagesListing) {
            sequenceNo++;
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setSequenceNoCellValue(borderCellStyle, row, 0, sequenceNo);
            setCellValue(borderCellStyle, seedUsage, row, 1, "seedType");
            setCellValue(borderCellStyle, seedUsage, row, 2, "seedName");
            setCellValue(borderCellStyle, seedUsage, row, 3, "nurseryManufacturer");
            setCellValue(borderCellStyle, seedUsage, row, 4, "vendorName");
            setCellValue(borderCellStyle, seedUsage, row, 5, "seedSku");
        }
    }

    private void fillSprayingUsageListingData(XSSFSheet sheet, CellStyle borderCellStyle, List<Tuple> sprayingUsagesListing) {
        int rowIdx = 2; // after header row
        int sequenceNo = 0;
        for (Tuple spraying : sprayingUsagesListing) {
            sequenceNo++;
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setSequenceNoCellValue(borderCellStyle, row, 0, sequenceNo);
            setCellValue(borderCellStyle, spraying, row, 1, "sprayingName");
            setCellValue(borderCellStyle, spraying, row, 2, "sprayingIngredient");
            setCellValue(borderCellStyle, spraying, row, 3, "sprayingManufacturer");
            setCellValue(borderCellStyle, spraying, row, 4, "vendorName");
            setCellValue(borderCellStyle, spraying, row, 5, "sprayingPurpose");
        }
    }

    private void fillManuringUsageListingData(XSSFSheet sheet, CellStyle borderCellStyle, List<Tuple> manuringUsagesListing) {
        int rowIdx = 2; // after header row
        int sequenceNo = 0;
        for (Tuple manuring : manuringUsagesListing) {
            sequenceNo++;
            rowIdx++;
            Row row = sheet.createRow(rowIdx);
            setSequenceNoCellValue(borderCellStyle, row, 0, sequenceNo);
            setCellValue(borderCellStyle, manuring, row, 1, "manuringName");
            setCellValue(borderCellStyle, manuring, row, 2, "manuringIngredient");
            setCellValue(borderCellStyle, manuring, row, 3, "manuringManufacturer");
            setCellValue(borderCellStyle, manuring, row, 4, "vendorName");
            setCellValue(borderCellStyle, manuring, row, 5, "manuringPurpose");
        }
    }

    private void fillHarvestData(XSSFSheet harvestSheet, CellStyle datetimeCellStyle, CellStyle borderCellStyle, List<Tuple> harvestData) {
        int rowIdx = 2; // after header row
        for (Tuple harvest : harvestData) {
            rowIdx++;
            Row row = harvestSheet.createRow(rowIdx);
            setCellValue(datetimeCellStyle, harvest, row, 0, "harvestExecuteDate");
            setCellValue(borderCellStyle, harvest, row, 1, "netHouseName");
            setCellValue(borderCellStyle, harvest, row, 2, "cropCode");
            setCellValue(borderCellStyle, harvest, row, 3, "harvestQuantityLv1");
            setCellValue(borderCellStyle, harvest, row, 4, "harvestQuantityLv2");
            setCellValue(borderCellStyle, harvest, row, 5, "harvestQuantityLv3");
            setCellValue(borderCellStyle, harvest, row, 6, "harvestRemovedQuantity");
            setCellValue(borderCellStyle, harvest, row, 7, "executeBy");
        }
    }

    private void fillSprayingData(XSSFSheet sprayingSheet, CellStyle datetimeCellStyle, CellStyle borderCellStyle, List<Tuple> sprayingHistoryData) {
        int rowIdx = 2; // after header row
        for (Tuple spraying : sprayingHistoryData) {
            rowIdx++;
            Row row = sprayingSheet.createRow(rowIdx);
            setCellValue(datetimeCellStyle, spraying, row, 0, "sprayingExecuteDate");
            setCellValue(borderCellStyle, spraying, row, 1, "cropCode");
            setCellValue(borderCellStyle, spraying, row, 2, "sprayingPurpose");
            setCellValue(borderCellStyle, spraying, row, 3, "cropProtectionName");
            setCellValue(borderCellStyle, spraying, row, 4, "cropProtectionIngredient");
            setCellValue(borderCellStyle, spraying, row, 5, "cropProtectionDosage");
            setCellValue(borderCellStyle, spraying, row, 6, "cropProtectionQuantity");
            setCellValue(borderCellStyle, spraying, row, 7, "cropProtectionHowToUse");
            setCellValue(borderCellStyle, spraying, row, 8, "equipmentId");
            setCellValue(borderCellStyle, spraying, row, 9, "sprayingWeatherConditions");
            setCellValue(borderCellStyle, spraying, row, 10, "sprayingQuarantinePeriod");
            setCellValue(borderCellStyle, spraying, row, 11, "executeBy");
        }
    }

    private void fillPlantingData(XSSFSheet dataSheet, CellStyle datetimeCellStyle, CellStyle borderCellStyle, List<Tuple> plantingData) {
        int rowIdx = 2; // after header row
        for (Tuple planting : plantingData) {
            rowIdx++;
            Row row = dataSheet.createRow(rowIdx);
            setCellValue(borderCellStyle, planting, row, 0, "netHouseName");
            setCellValue(datetimeCellStyle, planting, row, 1, "nurseryExecuteDate");
            setCellValue(borderCellStyle, planting, row, 2, "seedName");
            setCellValue(borderCellStyle, planting, row, 3, "nurseryQuantity");
            setCellValue(borderCellStyle, planting, row, 4, "plantingQuantity");
            setCellValue(datetimeCellStyle, planting, row, 5, "plantingExecuteDate");
            setCellValue(borderCellStyle, planting, row, 6, "cropCode");
            setCellValue(borderCellStyle, planting, row, 7, "plantingDescription");
        }
    }

    private void setCellValue(CellStyle cellStyle, Tuple data, Row row, int cellIndex, String fieldName, Boolean isBooleanField) {
        Cell cell = row.createCell(cellIndex);
        cell.setCellStyle(cellStyle);
        char st = '\u2713'; //check mark
        if (data.get(fieldName) != null) {
            Class<?> clazz = data.get(fieldName).getClass();
            if (clazz == Double.class) {
                Double fieldValue = Double.valueOf((Double) data.get(fieldName));
                cell.setCellValue(fieldValue);
            } else if (clazz == Integer.class || clazz == BigInteger.class) {
                Long fieldValue;
                if (clazz == BigInteger.class) {
                    fieldValue = ((BigInteger) data.get(fieldName)).longValue();
                } else {
                    fieldValue = ((Integer) data.get(fieldName)).longValue();
                }
                // bool field
                if (isBooleanField) {
                    Boolean blnFieldValue = fieldValue == 1 ? true : false;
                    if (blnFieldValue) {
                        cell.setCellValue(String.valueOf(st));
                    }
                } else {
                    cell.setCellValue(fieldValue);
                }
            } else if (clazz == Boolean.class) {
                Boolean fieldValue = Boolean.valueOf((Boolean) data.get(fieldName));
                if (fieldValue) {
                    cell.setCellValue(String.valueOf(st));
                }
            } else if (clazz == java.sql.Date.class) {
                java.sql.Date fieldValue = (java.sql.Date) data.get(fieldName);
                cell.setCellValue(fieldValue);
            } else {
                String fieldValue = String.valueOf(data.get(fieldName));
                cell.setCellValue(fieldValue);
            }

        }
    }

    private void setCellValue(
        CellStyle cellStyle,
        Tuple data,
        Row row,
        int cellIndex,
        String fieldName) {
        setCellValue(cellStyle, data, row, cellIndex, fieldName, false);
    }


    private void setSequenceNoCellValue(CellStyle cellStyle, Row row, int cellIndex, int cellValue) {
        Cell cell = row.createCell(cellIndex);
        cell.setCellStyle(cellStyle);
        cell.setCellValue(cellValue);
    }

    private void setStringCellValue(CellStyle cellStyle, Row row, int cellIndex, String cellValue) {
        Cell cell = row.createCell(cellIndex);
        cell.setCellStyle(cellStyle);
        cell.setCellValue(cellValue);
    }

    private List<Tuple> getMonthlyActivityData(Integer month, Integer year, Long customerId, Long cropId) {
        String sqlString =
            "SELECT \n" +
                "    DATE(activity.execute_date) AS activityDate,\n" +
                "    COUNT(*) AS activityCount\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND crop.id = CASE\n" +
                "        WHEN :cropId IS NULL THEN crop.id\n" +
                "        ELSE :cropId\n" +
                "    END\n" +
                "        AND MONTH(activity.execute_date) = :month\n" +
                "        AND YEAR(activity.execute_date) = :year\n" +
                "GROUP BY activityDate\n" +
                "ORDER BY activityDate ASC\n";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("month", month)
            .setParameter("year", year)
            .setParameter("customerId", customerId)
            .setParameter("cropId", cropId);

        return query.getResultList();
    }

    private List getSeedUsagesData(Long customerId) {
        String sqlString =
            "SELECT \n" +
                "    supply.id AS seedId,\n" +
                "    supply.sku AS seedSku,\n" +
                "    supply.name AS seedName,\n" +
                "    SUM(nursery.quantity) AS seedQuantityUsage\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supply ON crop.tb_supplies_id = supply.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        INNER JOIN\n" +
                "    tb_nursery AS nursery ON nursery.tb_activity_id = activity.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId \n" +
                "GROUP BY seedId , seedSku , seedName\n";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId);
        return query.getResultList();
    }

    private List<Tuple> getPlantingData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            " SELECT \n" +
                "    entity.id AS netHouseId,\n" +
                "    entity.name AS netHouseName,\n" +
                "    DATE(nursery.nurseryExecuteDate) AS nurseryExecuteDate,\n" +
                "    supply.name AS seedName,\n" +
                "    nursery.nurseryQuantity AS nurseryQuantity,\n" +
                "    planting.plantingQuantity AS plantingQuantity,\n" +
                "    DATE(planting.plantingExecuteDate) AS plantingExecuteDate,\n" +
                "    crop.id AS cropId,\n" +
                "    crop.code AS cropCode,\n" +
                "    planting.description AS plantingDescription\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supply ON crop.tb_supplies_id = supply.id\n" +
                "        INNER JOIN\n" +
                "    (SELECT \n" +
                "        activity.execute_date AS nurseryExecuteDate,\n" +
                "            activity.tb_crop_id AS tb_crop_id,\n" +
                "            nursery.quantity AS nurseryQuantity\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    LEFT JOIN tb_nursery AS nursery ON nursery.tb_activity_id = activity.id\n" +
                "    WHERE\n" +
                "        activityType.name = :nurseryActivityType) nursery ON nursery.tb_crop_id = crop.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        activity.execute_date AS plantingExecuteDate,\n" +
                "            activity.tb_crop_id AS tb_crop_id,\n" +
                "            activity.description AS description,\n" +
                "            planting.quantity AS plantingQuantity\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    LEFT JOIN tb_planting AS planting ON planting.tb_activity_id = activity.id\n" +
                "    WHERE\n" +
                "        activityType.name = :plantingActivityType) planting ON planting.tb_crop_id = crop.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND DATE(plantingExecuteDate) BETWEEN DATE(:startDate) AND DATE(:endDate) ";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            //.setParameter("nurseryActivityType", ACTIVE_TYPE_NURSERY.name())
            //.setParameter("plantingActivityType", ACTIVE_TYPE_STATUS_GROW.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getSprayingHistoryData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    DATE(activity.execute_date) AS sprayingExecuteDate,\n" +
                "    crop.id AS cropId,\n" +
                "    crop.code AS cropCode,\n" +
                "    COALESCE(spraying.purpose, manuring.purpose) AS purpose,\n" +
                "    spraying.purpose AS sprayingPurpose,\n" +
                "    manuring.purpose AS manuringPurpose,\n" +
                "    supply.name AS cropProtectionName,\n" +
                "    supply.ingredient AS cropProtectionIngredient,\n" +
                "    suppliesUsingDetails.dosage AS cropProtectionDosage,\n" +
                "    suppliesUsingDetails.quantity AS cropProtectionQuantity,\n" +
                "    suppliesUsingDetails.how_to_use AS cropProtectionHowToUse,\n" +
                "    equipmentOfCustomer.id AS equipmentId,\n" +
                "    spraying.weather_conditions AS sprayingWeatherConditions,\n" +
                "    spraying.quarantine_period AS sprayingQuarantinePeriod,\n" +
                "    manuring.weather_conditions AS manuringWeatherConditions,\n" +
                "    manuring.quarantine_period AS manuringQuarantinePeriod,\n" +
                "    detailUser.full_name AS executeBy\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        LEFT JOIN\n" +
                "    tb_spraying AS spraying ON spraying.tb_activity_id = activity.id\n" +
                "        LEFT JOIN\n" +
                "    tb_manuring AS manuring ON manuring.tb_activity_id = activity.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies_using_details AS suppliesUsingDetails ON suppliesUsingDetails.tb_spraying_id = spraying.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies_in_warehouse AS suppliesInWarehouse ON suppliesUsingDetails.tb_supplies_in_warehouse_id = suppliesInWarehouse.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supply ON suppliesInWarehouse.tb_supplies_id = supply.id\n" +
                "        LEFT JOIN\n" +
                "    tb_equipment_of_customer AS equipmentOfCustomer ON suppliesUsingDetails.tb_equipment_of_customer_id = equipmentOfCustomer.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "        AND activityType.name IN (:activityTypeName)\n";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            //.setParameter("activityTypeName", ACTIVE_TYPE_SPRAY.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getHarvestData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    DATE(activity.execute_date) AS harvestExecuteDate,\n" +
                "    entity.name AS netHouseName,\n" +
                "    crop.code AS cropCode,\n" +
                "    harvest.collected_quantity_lv_1 AS harvestQuantityLv1,\n" +
                "    harvest.collected_quantity_lv_2 AS harvestQuantityLv2,\n" +
                "    harvest.collected_quantity_lv_3 AS harvestQuantityLv3,\n" +
                "    harvest.removed_quantity AS harvestRemovedQuantity,\n" +
                "    detailUser.full_name AS executeBy\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        INNER JOIN\n" +
                "    tb_harvest AS harvest ON harvest.tb_activity_id = activity.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate) \n" +
                "        AND activityType.name = :activityTypeName\n";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            //.setParameter("activityTypeName", ACTIVE_TYPE_HARVEST.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getManuringUsageListingData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT DISTINCT\n" +
                "    supply.name AS manuringName,\n" +
                "    supply.ingredient AS manuringIngredient,\n" +
                "    supply.manufacturer AS manuringManufacturer,\n" +
                "    vendor.vendorName AS vendorName,\n" +
                "    manuring.purpose AS manuringPurpose\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        INNER JOIN\n" +
                "    tb_manuring AS manuring ON manuring.tb_activity_id = activity.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies_using_details AS suppliesUsingDetails ON suppliesUsingDetails.tb_manuring_id = manuring.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies_in_warehouse AS suppliesInWarehouse ON suppliesUsingDetails.tb_supplies_in_warehouse_id = suppliesInWarehouse.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supply ON suppliesInWarehouse.tb_supplies_id = supply.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT DISTINCT\n" +
                "        vendor.tb_customer_id AS customerId,\n" +
                "            suppliesOrderDetail.tb_supplies_id AS suppliesId,\n" +
                "            vendor.name AS vendorName\n" +
                "    FROM\n" +
                "        tb_vendor AS vendor\n" +
                "    INNER JOIN tb_supplies_order AS suppliesOrder ON suppliesOrder.tb_vendor_id = vendor.id\n" +
                "    INNER JOIN tb_supplies_order_details AS suppliesOrderDetail ON suppliesOrderDetail.tb_supplies_order_id = suppliesOrder.id) vendor ON supply.id = vendor.suppliesId\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name = :activityTypeName\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY manuringName\n";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            //.setParameter("activityTypeName", ACTIVE_TYPE_MANURING.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getSprayingUsageListingData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT DISTINCT\n" +
                "    supply.name AS sprayingName,\n" +
                "    supply.ingredient AS sprayingIngredient,\n" +
                "    supply.manufacturer AS sprayingManufacturer,\n" +
                "    vendor.vendorName AS vendorName,\n" +
                "    spraying.purpose AS sprayingPurpose\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        INNER JOIN\n" +
                "    tb_spraying AS spraying ON spraying.tb_activity_id = activity.id\n" +
                "            INNER JOIN\n" +
                "    tb_supplies_using_details AS suppliesUsingDetails ON suppliesUsingDetails.tb_spraying_id = spraying.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies_in_warehouse AS suppliesInWarehouse ON suppliesUsingDetails.tb_supplies_in_warehouse_id = suppliesInWarehouse.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supply ON suppliesInWarehouse.tb_supplies_id = supply.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT DISTINCT\n" +
                "        vendor.tb_customer_id AS customerId,\n" +
                "            suppliesOrderDetail.tb_supplies_id AS suppliesId,\n" +
                "            vendor.name AS vendorName\n" +
                "    FROM\n" +
                "        tb_vendor AS vendor\n" +
                "    INNER JOIN tb_supplies_order AS suppliesOrder ON suppliesOrder.tb_vendor_id = vendor.id\n" +
                "    INNER JOIN tb_supplies_order_details AS suppliesOrderDetail ON suppliesOrderDetail.tb_supplies_order_id = suppliesOrder.id) vendor ON supply.id = vendor.suppliesId\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name = :activityTypeName\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY sprayingName\n";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            //.setParameter("activityTypeName", ACTIVE_TYPE_SPRAY.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getSeedUsageListingData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT DISTINCT\n" +
                "    supply.name AS seedType,\n" +
                "    supply.name AS seedName,\n" +
                "    supply.manufacturer AS nurseryManufacturer,\n" +
                "    vendor.vendorName AS vendorName,\n" +
                "    supply.sku AS seedSku\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supply ON crop.tb_supplies_id = supply.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        INNER JOIN\n" +
                "    tb_nursery AS nursery ON nursery.tb_activity_id = activity.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT DISTINCT\n" +
                "        vendor.tb_customer_id AS customerId,\n" +
                "            suppliesOrderDetail.tb_supplies_id AS suppliesId,\n" +
                "            vendor.name AS vendorName\n" +
                "    FROM\n" +
                "        tb_vendor AS vendor\n" +
                "    INNER JOIN tb_supplies_order AS suppliesOrder ON suppliesOrder.tb_vendor_id = vendor.id\n" +
                "    INNER JOIN tb_supplies_order_details AS suppliesOrderDetail ON suppliesOrderDetail.tb_supplies_order_id = suppliesOrder.id) vendor ON supply.id = vendor.suppliesId\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name = :activityTypeName\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            //.setParameter("activityTypeName", ACTIVE_TYPE_NURSERY.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getSubstrateUsageListingData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT DISTINCT\n" +
                "    substrate.name AS substrateType,\n" +
                "    substrate.name AS substrateName,\n" +
                "    substrate.manufacturer AS substrateManufacturer,\n" +
                "    vendor.vendorName AS vendorName\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        INNER JOIN\n" +
                "    tb_nursery AS nursery ON nursery.tb_activity_id = activity.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS substrate ON nursery.substrates_id = substrate.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT DISTINCT\n" +
                "        vendor.tb_customer_id AS customerId,\n" +
                "            suppliesOrderDetail.tb_supplies_id AS suppliesId,\n" +
                "            vendor.name AS vendorName\n" +
                "    FROM\n" +
                "        tb_vendor AS vendor\n" +
                "    INNER JOIN tb_supplies_order AS suppliesOrder ON suppliesOrder.tb_vendor_id = vendor.id\n" +
                "    INNER JOIN tb_supplies_order_details AS suppliesOrderDetail ON suppliesOrderDetail.tb_supplies_order_id = suppliesOrder.id) vendor ON substrate.id = vendor.suppliesId\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name = :activityTypeName\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            //.setParameter("activityTypeName", ACTIVE_TYPE_NURSERY.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getSubstrateUsageHistoryData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    DATE(activity.execute_date) AS nurseryExecuteDate,\n" +
                "    substrate.name AS substrateType,\n" +
                "    crop.code AS substrateCropCode,\n" +
                "    '' AS quantity,\n" +
                "    '' AS plantingCropCode,\n" +
                "    detailUser.full_name AS executeBy\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        INNER JOIN\n" +
                "    tb_nursery AS nursery ON nursery.tb_activity_id = activity.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS substrate ON nursery.substrates_id = substrate.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name = :activityTypeName\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY nurseryExecuteDate";
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            //.setParameter("activityTypeName", ACTIVE_TYPE_NURSERY.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getActivityHistoryData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    DATE(activity.execute_date) AS executeDate,\n" +
                "    entity.id AS netHouseId,\n" +
                "    entity.name AS netHouseName,\n" +
                "    crop.code AS cropCode,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypeCleanGarden THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isCleanGarden,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypePrepareGarden THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isPrepareGarden,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypePlanting THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isPlanting,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypeHangVines THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isHangVines,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypePollination THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isPollination,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypeSpraying THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isSpraying,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypeHarvest THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isHarvest,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypeCleanHarvestTools THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isCleanHarvestTools,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypeCleanHandBeforeHarvest THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isCleanHandBeforeHarvest,\n" +
                "    CASE\n" +
                "        WHEN activityType.name = :activityTypeOther THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isOtherActivity,\n" +
                "    detailUser.full_name AS executeBy,\n" +
                "    activity.description AS description\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name IN (:activityTypeNames)\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY activity.execute_date , entity.id, crop.code\n";

//        List<String> activityTypeNames = new ArrayList<>(
//            Arrays.asList(
//                ACTIVE_TYPE_CLEAN_THE_GARDEN.name(),
//                ACTIVE_TYPE_PREPARE_THE_GARDEN.name(),
//                ACTIVE_TYPE_STATUS_GROW.name(),
//                ACTIVE_TYPE_HANG_SECONDS.name(),
//                ACTIVE_TYPE_POLLINATION.name(),
//                ACTIVE_TYPE_SPRAY.name(),
//                ACTIVE_TYPE_HARVEST.name(),
//                ACTIVE_TYPE_CLEAN_HARVESTING_TOOLS.name(),
//                ACTIVE_TYPE_CLEAN_HANDS_BEFORE_HARVEST.name(),
//                ACTIVE_TYPE_OTHER.name()));
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
//            .setParameter("activityTypeCleanGarden", ACTIVE_TYPE_CLEAN_THE_GARDEN.name())
//            .setParameter("activityTypePrepareGarden", ACTIVE_TYPE_PREPARE_THE_GARDEN.name())
//            .setParameter("activityTypePlanting", ACTIVE_TYPE_STATUS_GROW.name())
//            .setParameter("activityTypeHangVines", ACTIVE_TYPE_HANG_SECONDS.name())
//            .setParameter("activityTypePollination", ACTIVE_TYPE_POLLINATION.name())
//            .setParameter("activityTypeSpraying", ACTIVE_TYPE_SPRAY.name())
//            .setParameter("activityTypeHarvest", ACTIVE_TYPE_HARVEST.name())
//            .setParameter("activityTypeCleanHarvestTools", ACTIVE_TYPE_CLEAN_HARVESTING_TOOLS.name())
//            .setParameter("activityTypeCleanHandBeforeHarvest", ACTIVE_TYPE_CLEAN_HANDS_BEFORE_HARVEST.name())
//            .setParameter("activityTypeOther", ACTIVE_TYPE_OTHER.name())
//            .setParameter("activityTypeNames", activityTypeNames)
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getPlantHeathAndPestStatusData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    DATE(activity.execute_date) AS executeDate,\n" +
                "    crop.code AS cropCode,\n" +
                "    plantHeath.cropStatus AS cropStatus,\n" +
                "    pestInvestigate.pestStatus as pestStatus,\n" +
                "    detailUser.full_name AS executeBy,\n" +
                "    activity.description AS description\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        objectUpdate.tb_activity_id AS activityId,\n" +
                "            GROUP_CONCAT(DISTINCT CONCAT(objectParameter.description, ': ', objectUpdate.jhi_index)\n" +
                "                ORDER BY activityType.id\n" +
                "                SEPARATOR ', ') AS cropStatus" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_object_update AS objectUpdate ON objectUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_object_parameter AS objectParameter ON objectUpdate.tb_object_parameter_id = objectParameter.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeCropStatus\n" +
                "    GROUP BY activityId) plantHeath ON plantHeath.activityId = activity.id\n" +
                "      LEFT JOIN\n" +
                "    (SELECT \n" +
                "        objectUpdate.tb_activity_id AS activityId,\n" +
                "          GROUP_CONCAT(DISTINCT CONCAT(objectParameter.name, '@@', objectUpdate.jhi_index)\n" +
                "                ORDER BY activityType.id\n" +
                "                SEPARATOR \"|\") AS pestStatus\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_object_update AS objectUpdate ON objectUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_object_parameter AS objectParameter ON objectUpdate.tb_object_parameter_id = objectParameter.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypePestsInvestigation\n" +
                "    GROUP BY activityId) pestInvestigate ON pestInvestigate.activityId = activity.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name IN (:activityTypeNames)\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY activity.execute_date , crop.code\n";

//        List<String> activityTypeNames = new ArrayList<>(
//            Arrays.asList(
//                ACTIVE_TYPE_STATUS_CROP.name(),
//                ACTIVE_TYPE_PESTS_INVESTIGATION.name()));
        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
//            .setParameter("activityTypeCropStatus", ACTIVE_TYPE_STATUS_CROP.name())
//            .setParameter("activityTypePestsInvestigation", ACTIVE_TYPE_PESTS_INVESTIGATION.name())
//            .setParameter("activityTypeNames", activityTypeNames)
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getWaterUsageData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    DATE(activity.execute_date) AS executeDate,\n" +
                "    water.amount AS waterAmount,\n" +
                "    CASE\n" +
                "        WHEN water.water_type = :typeWateringPlants THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isPlantsWater,\n" +
                "    CASE\n" +
                "        WHEN water.water_type = :typeWaterDomestic THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isDomesticWater,\n" +
                "    CASE\n" +
                "        WHEN water.water_type = :typeWaterOther THEN 1\n" +
                "        ELSE 0\n" +
                "    END AS isOtherWater,\n" +
                "    detailUser.full_name AS executeBy\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        INNER JOIN\n" +
                "    tb_water AS water ON water.tb_activity_id = activity.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name = :activityTypeName\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY executeDate\n";

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
//            .setParameter("typeWateringPlants", TYPE_WATERING_PLANTS.name())
//            .setParameter("typeWaterDomestic", TYPE_WATER_DOMESTIC.name())
//            .setParameter("typeWaterOther", TYPE_OTHER.name())
            .setParameter("customerId", customerId)
//            .setParameter("activityTypeName", ACTIVE_TYPE_USE_WATER.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getImportedMaterialData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT DISTINCT\n" +
                "    DATE(stockUpdate.update_date) AS stockUpdateDate,\n" +
                "    suppliesType.description AS suppliesTypeDescription,\n" +
                "    supply.name AS supplyName,\n" +
                "    supply.unit AS supplyUnit,\n" +
                "    stockUpdate.quantity AS stockUpdateQuantity,\n" +
                "    vendor.vendorName AS vendorName,\n" +
                "    detailUser.full_name AS executeBy,\n" +
                "    stockUpdate.description AS stockUpdateDescription\n" +
                "FROM\n" +
                "    tb_stock_update AS stockUpdate\n" +
                "        INNER JOIN\n" +
                "    tb_supplies_in_warehouse AS suppliesInWarehouse ON stockUpdate.tb_supplies_in_warehouse_id = suppliesInWarehouse.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supply ON suppliesInWarehouse.tb_supplies_id = supply.id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies_type AS suppliesType ON supply.tb_supplies_type_id = suppliesType.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON suppliesInWarehouse.created_by = detailUser.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT DISTINCT\n" +
                "        vendor.tb_customer_id AS customerId,\n" +
                "            suppliesOrder.id AS suppliesOrderId,\n" +
                "            suppliesOrderDetail.tb_supplies_id AS suppliesId,\n" +
                "            vendor.name AS vendorName\n" +
                "    FROM\n" +
                "        tb_vendor AS vendor\n" +
                "    INNER JOIN tb_supplies_order AS suppliesOrder ON suppliesOrder.tb_vendor_id = vendor.id\n" +
                "    INNER JOIN tb_supplies_order_details AS suppliesOrderDetail ON suppliesOrderDetail.tb_supplies_order_id = suppliesOrder.id) vendor ON suppliesInWarehouse.tb_supplies_id = vendor.suppliesOrderId\n" +
                "WHERE\n" +
                "    supply.tb_customer_id = :customerId\n" +
                "        AND stockUpdate.action = :stockUpdateAction\n" +
                "        AND DATE(stockUpdate.update_date) BETWEEN DATE(:startDate) AND DATE(:endDate)";

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            .setParameter("stockUpdateAction", 0)
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getImportExportWarehouseData(Long customerId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    DATE(activity.execute_date) AS executeDate,\n" +
                "    crop.code AS cropCode,\n" +
                "    harvest.harvestQuantityLv1 AS harvestQuantityLv1,\n" +
                "    harvest.harvestQuantityLv2 AS harvestQuantityLv2,\n" +
                "    harvest.harvestuantityLv3 AS harvestuantityLv3,\n" +
                "    sell.sellQuantityLv1 AS sellQuantityLv1,\n" +
                "    sell.sellQuantityLv2 AS sellQuantityLv2,\n" +
                "    sell.sellQuantityLv3 AS sellQuantityLv3,\n" +
                "    sell.buyer AS buyer,\n" +
                "    detailUser.full_name AS executeBy\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        harvest.tb_activity_id AS activityId,\n" +
                "            harvest.collected_quantity_lv_1 AS harvestQuantityLv1,\n" +
                "            harvest.collected_quantity_lv_2 AS harvestQuantityLv2,\n" +
                "            harvest.collected_quantity_lv_3 AS harvestuantityLv3\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_harvest AS harvest ON harvest.tb_activity_id = activity.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeHarvest) harvest ON harvest.activityId = activity.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        sell.tb_activity_id AS activityId,\n" +
                "            sell.quantity_lv_1 AS sellQuantityLv1,\n" +
                "            sell.quantity_lv_2 AS sellQuantityLv2,\n" +
                "            sell.quantity_lv_3 AS sellQuantityLv3,\n" +
                "            sell.buyer AS buyer\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_sell AS sell ON sell.tb_activity_id = activity.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeSell) sell ON sell.activityId = activity.id\n" +
                "        LEFT JOIN\n" +
                "    tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND activityType.name IN (:activityTypeNames)\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY activity.execute_date , crop.code\n";

//        List<String> activityTypeNames = new ArrayList<>(
//            Arrays.asList(
//                ACTIVE_TYPE_HARVEST.name(),
//                ACTIVE_TYPE_SELL.name()));

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
//            .setParameter("activityTypeHarvest", ACTIVE_TYPE_HARVEST.name())
//            .setParameter("activityTypeSell", ACTIVE_TYPE_SELL.name())
//            .setParameter("activityTypeNames", activityTypeNames)
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private List<Tuple> getImportExportSuppliesInWarehouseData(
        ReportImportExportSuppliesInWareHouseCriteria criteria) {
        String subtractSumStockSubSqlString = 
            " (SELECT \n" + 
                " ( \n" +
                    " SUM( \n" +
                        " CASE \n" +
                            " WHEN subTu.action = 0 \n" +
                            " THEN subTu.quantity \n" +
                            " ELSE 0 \n" +
                        " END \n" +
                    " ) - SUM( \n" +
                        " CASE \n" +
                            " WHEN subTu.action = 1 \n" +
                            " THEN subTu.quantity \n" +
                            " ELSE 0 \n" +
                        " END \n" +
                    " ) \n" +
                " ) \n" +
                " FROM tb_stock_update subTu \n" +
                " WHERE DATE(subTu.update_date) < sug.updateDate \n" +
                " AND subTu.tb_block_details_id = sug.blockId \n" +
                " GROUP BY subTu.tb_block_details_id) \n";

        String stockUpdateGroupedSqlString = 
            " (SELECT \n" +
            " s.tb_supplies_type_id AS supplyTypeId, \n" +
            " bd.id AS blockId, \n" +
            " s.tb_customer_id AS customerId, \n" +
            " DATE(tu.update_date) AS updateDate, \n" +
            " SUM( \n" +
                " CASE \n" +
                    " WHEN tu.action = 0 \n" +
                    " THEN tu.quantity \n" +
                    " ELSE 0 \n" +
                " END \n" +
            " ) AS quantityIncomingStock, \n" +
            " SUM( \n" +
                " CASE \n" +
                    " WHEN tu.action = 1 \n" +
                    " THEN tu.quantity \n" +
                    " ELSE 0 \n" +
                " END \n" +
            " ) AS quantityOutgoingStock \n" +
            " FROM tb_stock_update tu \n" +
            " INNER JOIN tb_block_details bd ON tu.tb_block_details_id = bd.id \n" + 
            " INNER JOIN tb_supplies_in_warehouse siw ON tu.tb_supplies_in_warehouse_id = siw.id \n" +
            " LEFT JOIN tb_supplies s ON siw.tb_supplies_id = s.id \n" +
            " GROUP BY updateDate, blockId, customerId, supplyTypeId \n" +
            " ORDER BY updateDate ) ";
        
        String sqlString = "SELECT \n" +
            " sug.supplyTypeId, \n" +
            " st.description as supplyTypeDescription, \n" +
            " sug.customerId, \n" +
            " sug.blockId, \n" +
            " bd.code as blockCode, \n" +
            " sug.updateDate, \n" +
            subtractSumStockSubSqlString + " as beginningStock, \n" +
            " sug.quantityIncomingStock, \n" +
            " sug.quantityOutgoingStock, \n" +
            " ((" + subtractSumStockSubSqlString + " + sug.quantityIncomingStock ) - sug.quantityOutgoingStock) as endingStock \n" +
            " FROM " + stockUpdateGroupedSqlString + " AS sug " +
            " LEFT JOIN tb_supplies_type st ON sug.supplyTypeId = st.id \n" +
            " LEFT JOIN tb_block_details bd ON sug.blockId = bd.id \n" +
            " WHERE sug.customerId = :customerId\n "; 

        if (criteria.getEndDate() != null) {
            sqlString += " AND DATE(sug.updateDate) BETWEEN DATE(:startDate) AND DATE(:endDate) \n";
        } else {
            sqlString += " AND DATE(sug.updateDate) >= DATE(:startDate) \n";
        }

        if (criteria.getSupplyTypeId() != null) {
            sqlString += " AND sug.supplyTypeId = :supplyTypeId \n";
        }

        if (criteria.getSupplyTypeName() != null) {
            sqlString += " AND st.name = :supplyTypeName \n";
        }

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", criteria.getCustomerId())
            .setParameter("startDate", criteria.getStartDate())
            .setParameter("endDate", criteria.getEndDate());

        if (criteria.getSupplyTypeId() != null) {
            query = query.setParameter("supplyTypeId", criteria.getSupplyTypeId());
        }
        if (criteria.getSupplyTypeName() != null) {
            query = query.setParameter("supplyTypeName", criteria.getSupplyTypeName());
        }
        return query.getResultList();
    }

    private Tuple getNurseryMonitoringHeaderData(Long customerId, Long cropId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    entity.name AS netHouse,\n" +
                "    supplies.name AS seedName,\n" +
                "    nursery.seedIncubationTime,\n" +
                "    DATE(nursery.nurseryExecuteDate) AS nurseryExecuteDate,\n" +
                "    planting.plantingQuantity,\n" +
                "    plantHeath.currentPlants,\n" +
                "    plantHeath.currentPlantsDate,\n" +
                "    cropDetailUser.pics\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supplies ON crop.tb_supplies_id = supplies.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        crop.id AS cropId,\n" +
                "            GROUP_CONCAT(detailUser.full_name\n" +
                "                SEPARATOR ', ') AS pics\n" +
                "    FROM\n" +
                "        tb_assignment AS assignment\n" +
                "    INNER JOIN tb_crop AS crop ON assignment.tb_crops_id = crop.id\n" +
                "    INNER JOIN tb_detail_user AS detailUser ON assignment.tb_detail_users_id = detailUser.id\n" +
                "    WHERE\n" +
                "        crop.id = :cropId) cropDetailUser ON cropDetailUser.cropId = crop.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        crop.id AS cropId,\n" +
                "            nursery.seed_incubation_time AS seedIncubationTime,\n" +
                "            activity.execute_date AS nurseryExecuteDate\n" +
                "    FROM\n" +
                "        tb_crop AS crop\n" +
                "    INNER JOIN tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_nursery AS nursery ON nursery.tb_activity_id = activity.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeNursery\n" +
                "            AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "    ORDER BY activity.execute_date ASC\n" +
                "    LIMIT 1) nursery ON nursery.cropId = crop.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        crop.id AS cropId,\n" +
                "            SUM(planting.quantity) AS plantingQuantity\n" +
                "    FROM\n" +
                "        tb_entity AS entity\n" +
                "    INNER JOIN tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "    INNER JOIN tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_planting AS planting ON planting.tb_activity_id = activity.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypePlanting\n" +
                "            AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "            AND entity.tb_customer_id = :customerId\n" +
                "            AND crop.id = :cropId) planting ON planting.cropId = crop.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        crop.id AS cropId,\n" +
                "            objectUpdate.jhi_index AS currentPlants,\n" +
                "            DATE(activity.execute_date) AS currentPlantsDate\n" +
                "    FROM\n" +
                "        tb_crop AS crop\n" +
                "    INNER JOIN tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_object_update AS objectUpdate ON objectUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_object_parameter AS objectParameter ON objectUpdate.tb_object_parameter_id = objectParameter.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeStatusCrop\n" +
                "            AND objectParameter.name = :paramSoLuongLenCay\n" +
                "            AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "    ORDER BY activity.execute_date DESC\n" +
                "    LIMIT 1) plantHeath ON plantHeath.cropId = crop.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND crop.id = :cropId";

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            .setParameter("cropId", cropId)
//            .setParameter("activityTypeNursery", ACTIVE_TYPE_NURSERY.name())
//            .setParameter("activityTypePlanting", ACTIVE_TYPE_STATUS_GROW.name())
//            .setParameter("activityTypeStatusCrop", ACTIVE_TYPE_STATUS_CROP.name())
//            .setParameter("paramSoLuongLenCay", SO_LUONG_LEN_CAY.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        if (query.getResultList().isEmpty())
            return null;
        return (Tuple) query.getResultList().get(0);
    }

    private List<Tuple> getNurseryMonitoringData(Long customerId, Long cropId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT DISTINCT\n" +
                "    activity.age_day AS ageDay,\n" +
                "    CONCAT(IF(nursery.nurseryInfo IS NULL,\n" +
                "                '',\n" +
                "                CONCAT(nursery.nurseryInfo, '\\n\\n')),\n" +
                "            IF(envUpdate.envIndex IS NULL,\n" +
                "                '',\n" +
                "                CONCAT(envUpdate.envIndex, '\\n\\n')),\n" +
                "            IF(plantHeath.cropStatus IS NULL,\n" +
                "                '',\n" +
                "                CONCAT(plantHeath.cropStatus, '\\n\\n')),\n" +
                "            COALESCE(pestInvestigate.pestStatus, '')) AS plantHeath,\n" +
                "    spraying.treatmentMethod AS treatmentMethod,\n" +
                "    COALESCE(spraying.executeBy, '') AS executeBy,\n" +
                "    COALESCE(spraying.sprayingResultAt, '') AS sprayingResultAt\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        nurseryTbl.ageDay,\n" +
                "            GROUP_CONCAT(nurseryTbl.nurseryInfo\n" +
                "                SEPARATOR '\\n\\n') AS nurseryInfo,\n" +
                "            GROUP_CONCAT(DISTINCT nurseryTbl.executeBy\n" +
                "                SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "        nursery.tb_activity_id AS activityId,\n" +
                "            activity.age_day AS ageDay,\n" +
                "            detailUser.full_name AS executeBy,\n" +
                "            CONCAT('Loại giá thể ươm: ', substrate.name, '\\n', 'Chiều dài mầm khi ươm: ', nursery.seed_length, '\\n', GROUP_CONCAT(DISTINCT CONCAT('Công nhân: ', nurseryDetails.worker_name, ' ươm khây số ', nurseryDetails.tray_number)\n" +
                "                ORDER BY nurseryDetails.id\n" +
                "                SEPARATOR '\\n'), '\\nGhi chú: ', activity.description) AS nurseryInfo\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_nursery AS nursery ON nursery.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_supplies AS substrate ON nursery.substrates_id = substrate.id\n" +
                "    LEFT JOIN tb_nursery_details AS nurseryDetails ON nurseryDetails.tb_nursery_id = nursery.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeNursery\n" +
                "    GROUP BY activityId , substrate.name , nursery.seed_length) nurseryTbl\n" +
                "    GROUP BY nurseryTbl.ageDay) nursery ON nursery.ageDay = activity.age_day\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        cropStatusTbl.ageDay,\n" +
                "            GROUP_CONCAT(cropStatusTbl.cropStatus\n" +
                "                SEPARATOR '\\n\\n') AS cropStatus,\n" +
                "            GROUP_CONCAT(DISTINCT cropStatusTbl.executeBy\n" +
                "                SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "        objectUpdate.tb_activity_id AS activityId,\n" +
                "            activity.age_day AS ageDay,\n" +
                "            detailUser.full_name AS executeBy,\n" +
                "            CONCAT(GROUP_CONCAT(DISTINCT CONCAT(objectParameter.description, ': ', objectUpdate.jhi_index, IF(objectParameter.unit = '%', objectParameter.unit, ''))\n" +
                "                ORDER BY objectParameter.id\n" +
                "                SEPARATOR '; '), '\\nGhi chú: ', activity.description) AS cropStatus\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_object_update AS objectUpdate ON objectUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_object_parameter AS objectParameter ON objectUpdate.tb_object_parameter_id = objectParameter.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeStatusCrop\n" +
                "    GROUP BY activityId) cropStatusTbl\n" +
                "    GROUP BY cropStatusTbl.ageDay) plantHeath ON plantHeath.ageDay = activity.age_day\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        envUpdate.ageDay,\n" +
                "            GROUP_CONCAT(envUpdate.envIndex\n" +
                "                SEPARATOR '; ') AS envIndex\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "            activity.age_day AS ageDay,\n" +
                "            CONCAT(envParameter.description, ': ', MIN(envUpdate.jhi_index), '-', MAX(envUpdate.jhi_index), IF(envParameter.unit IS NOT NULL, CONCAT(' ', envParameter.unit), '')) AS envIndex\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_environment_update AS envUpdate ON envUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_environmental_parameter AS envParameter ON envUpdate.tb_environmental_parameter_id = envParameter.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeEnvUpdate\n" +
                "    GROUP BY activity.age_day , envParameter.description , envParameter.unit) envUpdate\n" +
                "    GROUP BY envUpdate.ageDay) envUpdate ON envUpdate.ageDay = activity.age_day\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        pestInvestTbl.ageDay,\n" +
                "            GROUP_CONCAT(pestInvestTbl.pestStatus\n" +
                "                SEPARATOR '\\n\\n') AS pestStatus,\n" +
                "            GROUP_CONCAT(DISTINCT pestInvestTbl.executeBy\n" +
                "                SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "        objectUpdate.tb_activity_id AS activityId,\n" +
                "            activity.age_day AS ageDay,\n" +
                "            detailUser.full_name AS executeBy,\n" +
                "            CONCAT(GROUP_CONCAT(DISTINCT CONCAT(objectParameter.description, ': ', objectUpdate.jhi_index, IF(objectParameter.unit = '%', objectParameter.unit, ''))\n" +
                "                ORDER BY objectParameter.id\n" +
                "                SEPARATOR '; '), '\\nGhi chú: ', activity.description) AS pestStatus\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_object_update AS objectUpdate ON objectUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_object_parameter AS objectParameter ON objectUpdate.tb_object_parameter_id = objectParameter.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypePestInvest\n" +
                "    GROUP BY activityId) pestInvestTbl\n" +
                "    GROUP BY pestInvestTbl.ageDay) pestInvestigate ON pestInvestigate.ageDay = activity.age_day\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        sprayingTbl.ageDay,\n" +
                "            GROUP_CONCAT(sprayingTbl.treatmentMethod\n" +
                "                SEPARATOR '\\n\\n') AS treatmentMethod,\n" +
                "            GROUP_CONCAT(DISTINCT DATE(sprayingTbl.sprayingResultAt)\n" +
                "                SEPARATOR '\\n') AS sprayingResultAt,\n" +
                "            GROUP_CONCAT(DISTINCT sprayingTbl.executeBy\n" +
                "                SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "        spraying.tb_activity_id AS activityId,\n" +
                "            activity.age_day AS ageDay,\n" +
                "            spraying.result_at AS sprayingResultAt,\n" +
                "            detailUser.full_name AS executeBy,\n" +
                "            CONCAT(GROUP_CONCAT(DISTINCT CONCAT('Tên thuốc: ', supply.name, ' (', suppliesUsingDetails.dosage, ')', '\\nSố lượng phun: ', suppliesUsingDetails.quantity, '\\nCách dùng: ', suppliesUsingDetails.how_to_use)\n" +
                "                ORDER BY activityType.id\n" +
                "                SEPARATOR '\\n'), '\\nThời tiết phun: ', spraying.weather_conditions, '\\nGhi chú: ', activity.description) AS treatmentMethod\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_spraying AS spraying ON spraying.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_supplies_using_details AS suppliesUsingDetails ON suppliesUsingDetails.tb_spraying_id = spraying.id\n" +
                "    INNER JOIN tb_supplies_in_warehouse AS suppliesInWarehouse ON suppliesUsingDetails.tb_supplies_in_warehouse_id = suppliesInWarehouse.id\n" +
                "    INNER JOIN tb_supplies AS supply ON suppliesInWarehouse.tb_supplies_id = supply.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeSpraying\n" +
                "    GROUP BY activityId , spraying.weather_conditions , sprayingResultAt) sprayingTbl\n" +
                "    GROUP BY sprayingTbl.ageDay) spraying ON spraying.ageDay = activity.age_day\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND crop.id = :cropId\n" +
                "        AND activityType.name IN (:activityTypeNames)\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY activity.age_day";

//        List<String> activityTypeNames = new ArrayList<>(
//            Arrays.asList(
//                ACTIVE_TYPE_NURSERY.name(),
//                ACTIVE_TYPE_UPDATE_ENV.name(),
//                ACTIVE_TYPE_STATUS_CROP.name(),
//                ACTIVE_TYPE_PESTS_INVESTIGATION.name(),
//                ACTIVE_TYPE_SPRAY.name()));

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            .setParameter("cropId", cropId)
//            .setParameter("activityTypeNursery", ACTIVE_TYPE_NURSERY.name())
//            .setParameter("activityTypeStatusCrop", ACTIVE_TYPE_STATUS_CROP.name())
//            .setParameter("activityTypeEnvUpdate", ACTIVE_TYPE_UPDATE_ENV.name())
//            .setParameter("activityTypePestInvest", ACTIVE_TYPE_PESTS_INVESTIGATION.name())
//            .setParameter("activityTypeSpraying", ACTIVE_TYPE_SPRAY.name())
//            .setParameter("activityTypeNames", activityTypeNames)
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    private Tuple getCultivateHistoryHeaderData(Long customerId, Long cropId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT \n" +
                "    entity.name AS netHouse,\n" +
                "    supplies.name AS seedName,\n" +
                "    nursery.seedIncubationTime,\n" +
                "    DATE(cropDetailUser.plantingExecuteDate) AS plantingExecuteDate,\n" +
                "    planting.plantingQuantity,\n" +
                "    plantHeath.currentPlants,\n" +
                "    plantHeath.currentPlantsDate,\n" +
                "    cropDetailUser.pics\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_supplies AS supplies ON crop.tb_supplies_id = supplies.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        crop.id AS cropId,\n" +
                "        crop.start_date AS plantingExecuteDate,\n" +
                "            GROUP_CONCAT(detailUser.full_name\n" +
                "                SEPARATOR ', ') AS pics\n" +
                "    FROM\n" +
                "        tb_assignment AS assignment\n" +
                "    INNER JOIN tb_crop AS crop ON assignment.tb_crops_id = crop.id\n" +
                "    INNER JOIN tb_detail_user AS detailUser ON assignment.tb_detail_users_id = detailUser.id\n" +
                "    WHERE\n" +
                "        crop.id = :cropId) cropDetailUser ON cropDetailUser.cropId = crop.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        crop.id AS cropId,\n" +
                "            nursery.seed_incubation_time AS seedIncubationTime\n" +
                "    FROM\n" +
                "        tb_crop AS crop\n" +
                "    INNER JOIN tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_nursery AS nursery ON nursery.tb_activity_id = activity.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeNursery\n" +
                "            AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "    ORDER BY activity.execute_date ASC\n" +
                "    LIMIT 1) nursery ON nursery.cropId = crop.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        crop.id AS cropId,\n" +
                "            SUM(planting.quantity) AS plantingQuantity\n" +
                "    FROM\n" +
                "        tb_entity AS entity\n" +
                "    INNER JOIN tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "    INNER JOIN tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_planting AS planting ON planting.tb_activity_id = activity.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypePlanting\n" +
                "            AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "            AND entity.tb_customer_id = :customerId\n" +
                "            AND crop.id = :cropId) planting ON planting.cropId = crop.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        crop.id AS cropId,\n" +
                "            objectUpdate.jhi_index AS currentPlants,\n" +
                "            DATE(activity.execute_date) AS currentPlantsDate\n" +
                "    FROM\n" +
                "        tb_crop AS crop\n" +
                "    INNER JOIN tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_object_update AS objectUpdate ON objectUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_object_parameter AS objectParameter ON objectUpdate.tb_object_parameter_id = objectParameter.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeStatusCrop\n" +
                "            AND objectParameter.name = :paramSoLuongLenCay\n" +
                "            AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "    ORDER BY activity.execute_date DESC\n" +
                "    LIMIT 1) plantHeath ON plantHeath.cropId = crop.id\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND crop.id = :cropId";

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            .setParameter("cropId", cropId)
//            .setParameter("activityTypeNursery", ACTIVE_TYPE_NURSERY.name())
//            .setParameter("activityTypePlanting", ACTIVE_TYPE_STATUS_GROW.name())
//            .setParameter("activityTypeStatusCrop", ACTIVE_TYPE_STATUS_CROP.name())
//            .setParameter("paramSoLuongLenCay", SO_LUONG_LEN_CAY.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        if (query.getResultList().isEmpty())
            return null;
        return (Tuple) query.getResultList().get(0);
    }

    private List<Tuple> getCultivateHistoryData(Long customerId, Long cropId, Date startDate, Date endDate) {
        String sqlString =
            "SELECT DISTINCT\n" +
                "    activity.age_day AS ageDay,\n" +
                "    CONCAT(" +
                "            IF(envUpdate.envIndex IS NULL,\n" +
                "                '',\n" +
                "                CONCAT(envUpdate.envIndex, '\\n\\n')),\n" +
                "            IF(plantHeath.cropStatus IS NULL,\n" +
                "                '',\n" +
                "                CONCAT(plantHeath.cropStatus, '\\n\\n')),\n" +
                "            COALESCE(pestInvestigate.pestStatus, '')) AS plantHeath,\n" +
                "    spraying.treatmentMethod AS treatmentMethod,\n" +
                "    COALESCE(spraying.executeBy, manuring.executeBy,'') AS executeBy,\n" +
                "    COALESCE(spraying.sprayingResultAt, manuring.quarantinePeriod, water.waterAmount, '') AS sprayingResultAt\n" +
                "FROM\n" +
                "    tb_entity AS entity\n" +
                "        INNER JOIN\n" +
                "    tb_crop AS crop ON entity.id = crop.tb_entity_id\n" +
                "        INNER JOIN\n" +
                "    tb_activity AS activity ON activity.tb_crop_id = crop.id\n" +
                "        INNER JOIN\n" +
                "    tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        cropStatusTbl.ageDay,\n" +
                "            GROUP_CONCAT(cropStatusTbl.cropStatus\n" +
                "                SEPARATOR '\\n\\n') AS cropStatus,\n" +
                "            GROUP_CONCAT(DISTINCT cropStatusTbl.executeBy\n" +
                "                SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "        objectUpdate.tb_activity_id AS activityId,\n" +
                "            activity.age_day AS ageDay,\n" +
                "            detailUser.full_name AS executeBy,\n" +
                "            CONCAT(GROUP_CONCAT(DISTINCT CONCAT(objectParameter.description, ': ', objectUpdate.jhi_index, IF(objectParameter.unit = '%', objectParameter.unit, ''))\n" +
                "                ORDER BY objectParameter.id\n" +
                "                SEPARATOR '; '), '\\nGhi chú: ', activity.description) AS cropStatus\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_object_update AS objectUpdate ON objectUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_object_parameter AS objectParameter ON objectUpdate.tb_object_parameter_id = objectParameter.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeStatusCrop\n" +
                "    GROUP BY activityId) cropStatusTbl\n" +
                "    GROUP BY cropStatusTbl.ageDay) plantHeath ON plantHeath.ageDay = activity.age_day\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        envUpdate.ageDay,\n" +
                "            GROUP_CONCAT(envUpdate.envIndex\n" +
                "                SEPARATOR '; ') AS envIndex\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "            activity.age_day AS ageDay,\n" +
                "            CONCAT(envParameter.description, ': ', MIN(envUpdate.jhi_index), '-', MAX(envUpdate.jhi_index), IF(envParameter.unit IS NOT NULL, CONCAT(' ', envParameter.unit), '')) AS envIndex\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_environment_update AS envUpdate ON envUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_environmental_parameter AS envParameter ON envUpdate.tb_environmental_parameter_id = envParameter.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeEnvUpdate\n" +
                "    GROUP BY activity.age_day , envParameter.description , envParameter.unit) envUpdate\n" +
                "    GROUP BY envUpdate.ageDay) envUpdate ON envUpdate.ageDay = activity.age_day\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        pestInvestTbl.ageDay,\n" +
                "            GROUP_CONCAT(pestInvestTbl.pestStatus\n" +
                "                SEPARATOR '\\n\\n') AS pestStatus,\n" +
                "            GROUP_CONCAT(DISTINCT pestInvestTbl.executeBy\n" +
                "                SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "        objectUpdate.tb_activity_id AS activityId,\n" +
                "            activity.age_day AS ageDay,\n" +
                "            detailUser.full_name AS executeBy,\n" +
                "            CONCAT(GROUP_CONCAT(DISTINCT CONCAT(objectParameter.description, ': ', objectUpdate.jhi_index, IF(objectParameter.unit = '%', objectParameter.unit, ''))\n" +
                "                ORDER BY objectParameter.id\n" +
                "                SEPARATOR '; '), '\\nGhi chú: ', activity.description) AS pestStatus\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_object_update AS objectUpdate ON objectUpdate.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_object_parameter AS objectParameter ON objectUpdate.tb_object_parameter_id = objectParameter.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypePestInvest\n" +
                "    GROUP BY activityId) pestInvestTbl\n" +
                "    GROUP BY pestInvestTbl.ageDay) pestInvestigate ON pestInvestigate.ageDay = activity.age_day\n" +
                "        LEFT JOIN\n" +
                "    (SELECT \n" +
                "        sprayingTbl.ageDay,\n" +
                "            GROUP_CONCAT(sprayingTbl.treatmentMethod\n" +
                "                SEPARATOR '\\n\\n') AS treatmentMethod,\n" +
                "            GROUP_CONCAT(DISTINCT CONCAT('Ngày ghi nhận kết quả: ', DATE(sprayingTbl.sprayingResultAt))\n" +
                "                SEPARATOR '\\n') AS sprayingResultAt,\n" +
                "            GROUP_CONCAT(DISTINCT sprayingTbl.executeBy\n" +
                "                SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "        (SELECT \n" +
                "        spraying.tb_activity_id AS activityId,\n" +
                "            activity.age_day AS ageDay,\n" +
                "            spraying.result_at AS sprayingResultAt,\n" +
                "            detailUser.full_name AS executeBy,\n" +
                "            CONCAT(GROUP_CONCAT(DISTINCT CONCAT('Tên thuốc: ', supply.name, ' (', suppliesUsingDetails.dosage, ')', '\\nSố lượng phun: ', suppliesUsingDetails.quantity, '\\nCách dùng: ', suppliesUsingDetails.how_to_use)\n" +
                "                ORDER BY activityType.id\n" +
                "                SEPARATOR '\\n'), '\\nThời tiết phun: ', spraying.weather_conditions, '\\nGhi chú: ', activity.description) AS treatmentMethod\n" +
                "    FROM\n" +
                "        tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_spraying AS spraying ON spraying.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_supplies_using_details AS suppliesUsingDetails ON suppliesUsingDetails.tb_spraying_id = spraying.id\n" +
                "    INNER JOIN tb_supplies_in_warehouse AS suppliesInWarehouse ON suppliesUsingDetails.tb_supplies_in_warehouse_id = suppliesInWarehouse.id\n" +
                "    INNER JOIN tb_supplies AS supply ON suppliesInWarehouse.tb_supplies_id = supply.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "        activityType.name = :activityTypeSpraying\n" +
                "    GROUP BY activityId , spraying.weather_conditions , sprayingResultAt) sprayingTbl\n" +
                "    GROUP BY sprayingTbl.ageDay) spraying ON spraying.ageDay = activity.age_day\n" +
                "       LEFT JOIN\n" +
                "    (SELECT \n" +
                "       manuringTbl.ageDay,\n" +
                "       GROUP_CONCAT(manuringTbl.manuringMethod\n" +
                "           SEPARATOR '\\n\\n') AS manuringMethod,\n" +
                "       GROUP_CONCAT(DISTINCT CONCAT('Ngày cách ly an toàn: ', manuringTbl.quarantinePeriod ,'ngày')\n" +
                "           SEPARATOR '\\n') AS quarantinePeriod,\n" +
                "       GROUP_CONCAT(DISTINCT manuringTbl.executeBy\n" +
                "           SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "       (SELECT \n" +
                "       manuring.tb_activity_id AS activityId,\n" +
                "       activity.age_day AS ageDay,\n" +
                "       manuring.quarantine_period AS quarantinePeriod,\n" +
                "       detailUser.full_name AS executeBy,\n" +
                "       CONCAT(GROUP_CONCAT(DISTINCT CONCAT('Tên phân bón: ', supply.name, ' (', suppliesUsingDetails.dosage, ')', '\\nSố lượng: ', suppliesUsingDetails.quantity, '\\nCách dùng: ', suppliesUsingDetails.how_to_use)\n" +
                "           ORDER BY activityType.id\n" +
                "           SEPARATOR '\\n'), '\\nThời tiết phun: ', manuring.weather_conditions, '\\nGhi chú: ', activity.description) AS manuringMethod\n" +
                "    FROM\n" +
                "       tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_manuring AS manuring ON manuring.tb_activity_id = activity.id\n" +
                "    INNER JOIN tb_supplies_using_details AS suppliesUsingDetails ON suppliesUsingDetails.tb_spraying_id = manuring.id\n" +
                "    INNER JOIN tb_supplies_in_warehouse AS suppliesInWarehouse ON suppliesUsingDetails.tb_supplies_in_warehouse_id = suppliesInWarehouse.id\n" +
                "    INNER JOIN tb_supplies AS supply ON suppliesInWarehouse.tb_supplies_id = supply.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "       activityType.name = 'ACTIVE_TYPE_MANURING'\n" +
                "    GROUP BY activityId , manuring.weather_conditions , quarantinePeriod) manuringTbl\n" +
                "    GROUP BY manuringTbl.ageDay) manuring ON manuring.ageDay = activity.age_day\n" +
                "       LEFT JOIN\n" +
                "    (SELECT\n" +
                "       waterTbl.ageDay,\n" +
                "           GROUP_CONCAT(DISTINCT CONCAT('Lượng nước tưới: ', waterTbl.waterAmount)\n" +
                "               SEPARATOR '\\n') AS waterAmount,\n" +
                "           GROUP_CONCAT(DISTINCT waterTbl.executeBy\n" +
                "               SEPARATOR ', ') AS executeBy\n" +
                "    FROM\n" +
                "    (SELECT \n" +
                "       water.tb_activity_id AS activityId,\n" +
                "       activity.age_day AS ageDay,\n" +
                "       water.amount AS waterAmount,\n" +
                "       detailUser.full_name AS executeBy\n" +
                "    FROM\n" +
                "       tb_activity AS activity\n" +
                "    INNER JOIN tb_activity_type AS activityType ON activity.activity_type = activityType.id\n" +
                "    INNER JOIN tb_water AS water ON water.tb_activity_id = activity.id\n" +
                "    LEFT JOIN tb_detail_user AS detailUser ON activity.created_by = detailUser.id\n" +
                "    WHERE\n" +
                "       activityType.name = :activityTypeUseWater AND water.water_type = :waterTypeNames\n" +
                "    GROUP BY activityId , waterAmount) waterTbl\n" +
                "    GROUP BY waterTbl.ageDay) water ON water.ageDay = activity.age_day\n" +
                "WHERE\n" +
                "    entity.tb_customer_id = :customerId\n" +
                "        AND crop.id = :cropId\n" +
                "        AND activityType.name IN (:activityTypeNames)\n" +
                "        AND DATE(activity.execute_date) BETWEEN DATE(:startDate) AND DATE(:endDate)\n" +
                "ORDER BY activity.age_day";

//        List<String> activityTypeNames = new ArrayList<>(
//            Arrays.asList(
//                ACTIVE_TYPE_NURSERY.name(),
//                ACTIVE_TYPE_UPDATE_ENV.name(),
//                ACTIVE_TYPE_STATUS_CROP.name(),
//                ACTIVE_TYPE_PESTS_INVESTIGATION.name(),
//                ACTIVE_TYPE_SPRAY.name()));

        Query query = em
            .createNativeQuery(sqlString, Tuple.class)
            .setParameter("customerId", customerId)
            .setParameter("cropId", cropId)
//            .setParameter("activityTypeUseWater", ACTIVE_TYPE_USE_WATER.name())
//            .setParameter("activityTypeStatusCrop", ACTIVE_TYPE_STATUS_CROP.name())
//            .setParameter("activityTypeEnvUpdate", ACTIVE_TYPE_UPDATE_ENV.name())
//            .setParameter("activityTypePestInvest", ACTIVE_TYPE_PESTS_INVESTIGATION.name())
//            .setParameter("activityTypeSpraying", ACTIVE_TYPE_SPRAY.name())
//            .setParameter("activityTypeNames", activityTypeNames)
//            .setParameter("waterTypeNames", TYPE_WATERING_PLANTS.name())
            .setParameter("startDate", startDate)
            .setParameter("endDate", endDate);

        return query.getResultList();
    }

    @Override
    public void downloadNurseryMonitoringReport(
        Long customerId,
        Long cropId,
        Date startDate,
        Date endDate,
        HttpServletResponse response) throws IOException {
        if (endDate == null) {
            endDate = Date.from(Instant.now());
        }
        Resource resource = resourceLoader.getResource(NURSERY_MONITORING_TEMPLATE_PATH);

        Tuple nurseryMonitoringHeaderData = getNurseryMonitoringHeaderData(customerId, cropId, startDate, endDate);
        if (nurseryMonitoringHeaderData == null) {
            return;
        }
        List<Tuple> nurseryMonitoringData = getNurseryMonitoringData(customerId, cropId, startDate, endDate);

        String netHouse = "";
        if (nurseryMonitoringHeaderData.get("netHouse") != null) {
            netHouse = String.valueOf(nurseryMonitoringHeaderData.get("netHouse"));
        }

        String seedName = "";
        if (nurseryMonitoringHeaderData.get("seedName") != null) {
            seedName = String.valueOf(nurseryMonitoringHeaderData.get("seedName"));
        }

        String seedIncubationTime = "";
        if (nurseryMonitoringHeaderData.get("seedIncubationTime") != null) {
            Double seedIncubationTimeObj = Double.valueOf((Double) nurseryMonitoringHeaderData.get("seedIncubationTime"));
            seedIncubationTime = seedIncubationTimeObj.toString();
        }

        String nurseryExecuteDate = "";
        if (nurseryMonitoringHeaderData.get("nurseryExecuteDate") != null) {
            java.sql.Date nurseryExecuteDateObj = (java.sql.Date) nurseryMonitoringHeaderData.get("nurseryExecuteDate");
            DateFormat dateFormat = new SimpleDateFormat(EXPORT_DATE_FORMAT);
            nurseryExecuteDate = dateFormat.format(nurseryExecuteDateObj);
        }

        String plantingQuantity = "";
        if (nurseryMonitoringHeaderData.get("plantingQuantity") != null) {
            Double plantingQuantityObj = (Double) nurseryMonitoringHeaderData.get("plantingQuantity");
            plantingQuantity = plantingQuantityObj.toString();
        }

        String currentPlants = "";
        if (nurseryMonitoringHeaderData.get("currentPlants") != null) {
            currentPlants = String.valueOf(nurseryMonitoringHeaderData.get("currentPlants"));
        }

        String currentPlantsDate = "";
        if (nurseryMonitoringHeaderData.get("currentPlantsDate") != null) {
            java.sql.Date currentPlantsDateObj = (java.sql.Date) nurseryMonitoringHeaderData.get("currentPlantsDate");
            DateFormat dateFormat = new SimpleDateFormat(EXPORT_DATE_FORMAT);
            currentPlantsDate = dateFormat.format(currentPlantsDateObj);
        }

        String pics = "";
        if (nurseryMonitoringHeaderData.get("pics") != null) {
            pics = String.valueOf(nurseryMonitoringHeaderData.get("pics"));
        }

        //XWPFDocument document = new XWPFDocument();
        XWPFDocument document = new XWPFDocument(resource.getInputStream());

        // Create new Paragraph
        for (XWPFParagraph p : document.getParagraphs()) {
            List<XWPFRun> runs = p.getRuns();
            if (runs == null) {
                continue;
            }
            for (XWPFRun r : runs) {
                String text = r.getText(0);
                if (text == null) {
                    continue;
                }
                if (text.contains("{net_house}")) {
                    text = text.replace("{net_house}", netHouse);
                    r.setText(text, 0);
                }
                if (text.contains("{seed_name}")) {
                    text = text.replace("{seed_name}", seedName);
                    r.setText(text, 0);
                }
                if (text.contains("{seed_tray_time}")) {
                    text = text.replace("{seed_tray_time}", nurseryExecuteDate);
                    r.setText(text, 0);
                }
                if (text.contains("{plant_quantity}")) {
                    text = text.replace("{plant_quantity}", plantingQuantity);
                    r.setText(text, 0);
                }
                if (text.contains("{current_plant}")) {
                    text = text.replace("{current_plant}", currentPlants);
                    r.setText(text, 0);
                }
                if (text.contains("{plant_date}")) {
                    text = text.replace("{plant_date}", currentPlantsDate);
                    r.setText(text, 0);
                }
                if (text.contains("{person_in_charge}")) {
                    text = text.replace("{person_in_charge}", pics);
                    r.setText(text, 0);
                }
            }
        }

        if (!document.getTables().isEmpty()) {
            XWPFTable tbl = document.getTables().get(0);

            for (Tuple item : nurseryMonitoringData) {
                XWPFTableRow row = tbl.createRow();
                for (int col = 0; col < 5; col++) {
                    switch (col) {
                        case 0: {
                            XWPFTableCell cell = row.getCell(col);
                            XWPFParagraph p = cell.getParagraphArray(0);
                            p.setAlignment(ParagraphAlignment.CENTER);
                            XWPFRun r = p.createRun();
                            r.setFontSize(12);
                            r.setText(String.valueOf(item.get("ageDay")));
                            break;
                        }
                        case 1: {
                            XWPFTableCell cell = row.getCell(col);
                            cell.setWidth("4000");
                            XWPFParagraph p = cell.getParagraphArray(0);
                            XWPFRun r = p.createRun();
                            r.setFontSize(12);
                            if (item.get("plantHeath") != null) {
                                String plantHeath = String.valueOf(item.get("plantHeath"));
                                breakLine(cell, r, plantHeath);
                            }
                            break;
                        }
                        case 2: {
                            XWPFTableCell cell = row.getCell(col);
                            XWPFParagraph p = cell.getParagraphArray(0);
                            XWPFRun r = p.createRun();
                            r.setFontSize(12);
                            if (item.get("treatmentMethod") != null) {
                                String treatmentMethod = String.valueOf(item.get("treatmentMethod"));
                                breakLine(cell, r, treatmentMethod);
                            }
                            break;
                        }
                        case 3: {
                            XWPFTableCell cell = row.getCell(col);
                            XWPFParagraph p = cell.getParagraphArray(0);
                            XWPFRun r = p.createRun();
                            r.setFontSize(12);
                            r.setText(String.valueOf(item.get("executeBy")));
                            break;
                        }
                        case 4: {
                            XWPFTableCell cell = row.getCell(col);
                            XWPFParagraph p = cell.getParagraphArray(0);
                            XWPFRun r = p.createRun();
                            DateFormat dateFormatter = new SimpleDateFormat(EXPORT_DATE_FORMAT);

                            r.setFontSize(12);
                            if (item.get("sprayingResultAt") != null) {
                                String sprayingResultAt = String.valueOf(item.get("sprayingResultAt"));
                                String[] lines = sprayingResultAt.split("\n");
                                for (int i = 0; i < lines.length; i++) {
                                    String sprayingResultDate = "";
                                    try {
                                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
                                        sprayingResultDate = dateFormatter.format(formatter.parse(lines[i]));
                                    } catch (DateTimeParseException ex) {
                                        // ignore exception
                                    } catch (ParseException e) {
                                        // ignore exception
                                    }

                                    if (i == 0) {
                                        r.setText(sprayingResultDate);
                                    } else {
                                        XWPFParagraph paragraph = cell.addParagraph();
                                        XWPFRun run = paragraph.createRun();
                                        run.setFontSize(12);
                                        run.setText(sprayingResultDate);
                                    }
                                }
                            }
                            break;
                        }
                    }

                }

            }
        }


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

    @Override
    public void downloadCultivateHistoryReport(
        Long customerId,
        Long cropId,
        Date startDate,
        Date endDate,
        HttpServletResponse response) throws IOException {
        if (endDate == null) {
            endDate = Date.from(Instant.now());
        }
        Resource resource = resourceLoader.getResource(NURSERY_MONITORING_TEMPLATE_PATH);

        Tuple cultivateHistoryHeaderData = getCultivateHistoryHeaderData(customerId, cropId, startDate, endDate);
        if (cultivateHistoryHeaderData == null) {
            return;
        }
        List<Tuple> CultivateHistoryData = getCultivateHistoryData(customerId, cropId, startDate, endDate);

        String netHouse = "";
        if (cultivateHistoryHeaderData.get("netHouse") != null) {
            netHouse = String.valueOf(cultivateHistoryHeaderData.get("netHouse"));
        }

        String seedName = "";
        if (cultivateHistoryHeaderData.get("seedName") != null) {
            seedName = String.valueOf(cultivateHistoryHeaderData.get("seedName"));
        }

        String seedIncubationTime = "";
        if (cultivateHistoryHeaderData.get("seedIncubationTime") != null) {
            Double seedIncubationTimeObj = Double.valueOf((Double) cultivateHistoryHeaderData.get("seedIncubationTime"));
            seedIncubationTime = seedIncubationTimeObj.toString();
        }

        String plantingExecuteDate = "";
        if (cultivateHistoryHeaderData.get("plantingExecuteDate") != null) {
            java.sql.Date plantingExecuteDateObj = (java.sql.Date) cultivateHistoryHeaderData.get("plantingExecuteDate");
            DateFormat dateFormat = new SimpleDateFormat(EXPORT_DATE_FORMAT);
            plantingExecuteDate = dateFormat.format(plantingExecuteDateObj);
        }

        String plantingQuantity = "";
        if (cultivateHistoryHeaderData.get("plantingQuantity") != null) {
            Double plantingQuantityObj = (Double) cultivateHistoryHeaderData.get("plantingQuantity");
            plantingQuantity = plantingQuantityObj.toString();
        }

        String currentPlants = "";
        if (cultivateHistoryHeaderData.get("currentPlants") != null) {
            currentPlants = String.valueOf(cultivateHistoryHeaderData.get("currentPlants"));
        }

        String currentPlantsDate = "";
        if (cultivateHistoryHeaderData.get("currentPlantsDate") != null) {
            java.sql.Date currentPlantsDateObj = (java.sql.Date) cultivateHistoryHeaderData.get("currentPlantsDate");
            DateFormat dateFormat = new SimpleDateFormat(EXPORT_DATE_FORMAT);
            currentPlantsDate = dateFormat.format(currentPlantsDateObj);
        }

        String pics = "";
        if (cultivateHistoryHeaderData.get("pics") != null) {
            pics = String.valueOf(cultivateHistoryHeaderData.get("pics"));
        }

        //XWPFDocument document = new XWPFDocument();
        XWPFDocument document = new XWPFDocument(resource.getInputStream());

        // Create new Paragraph
        for (XWPFParagraph p : document.getParagraphs()) {
            List<XWPFRun> runs = p.getRuns();
            if (runs == null) {
                continue;
            }
            for (XWPFRun r : runs) {
                String text = r.getText(0);
                if (text == null) {
                    continue;
                }
                if (text.contains("Vị trí nhà ươm:")) {
                    text = text.replace("Vị trí nhà ươm:", "Nhà màng số:");
                    r.setText(text, 0);
                }
                if (text.contains("Thời gian ngâm hạt:")) {
                    text = text.replace("Thời gian ngâm hạt:", "Ngày ươm hạt:");
                    r.setText(text, 0);
                }
                if (text.contains("Thời gian vô khây ươm:")) {
                    text = text.replace("Thời gian vô khây ươm:", "Ngày trồng:");
                    r.setText(text, 0);
                }
                if (text.contains("{net_house}")) {
                    text = text.replace("{net_house}", netHouse);
                    r.setText(text, 0);
                }
                if (text.contains("{seed_name}")) {
                    text = text.replace("{seed_name}", seedName);
                    r.setText(text, 0);
                }
                if (text.contains("{seed_incubation_time}")) {
                    text = text.replace("{seed_incubation_time}", seedIncubationTime);
                    r.setText(text, 0);
                }
                if (text.contains("{seed_tray_time}")) {
                    text = text.replace("{seed_tray_time}", plantingExecuteDate);
                    r.setText(text, 0);
                }
                if (text.contains("{plant_quantity}")) {
                    text = text.replace("{plant_quantity}", plantingQuantity);
                    r.setText(text, 0);
                }
                if (text.contains("{current_plant}")) {
                    text = text.replace("{current_plant}", currentPlants);
                    r.setText(text, 0);
                }
                if (text.contains("{plant_date}")) {
                    text = text.replace("{plant_date}", currentPlantsDate);
                    r.setText(text, 0);
                }
                if (text.contains("{person_in_charge}")) {
                    text = text.replace("{person_in_charge}", pics);
                    r.setText(text, 0);
                }
            }
        }

        if (!document.getTables().isEmpty()) {
            XWPFTable tbl = document.getTables().get(0);

            XWPFTableCell tbl_cell = tbl.getRows().get(0).getTableCells().get(4);
            for (XWPFParagraph p : tbl_cell.getParagraphs()) {
                List<XWPFRun> runs = p.getRuns();
                if (runs == null) {
                    continue;
                }
                for (XWPFRun r : runs) {
                    String text = r.getText(0);
                    if (text == null) {
                        continue;
                    }
                    if (text.contains("NGÀY GHI NHẬN KẾT QUẢ")) {
                        text = text.replace("NGÀY GHI NHẬN KẾT QUẢ", "GHI CHÚ");
                        r.setText(text, 0);
                    }
                }
            }
            for (Tuple item : CultivateHistoryData) {
                XWPFTableRow row = tbl.createRow();
                for (int col = 0; col < 5; col++) {
                    switch (col) {
                        case 0: {
                            XWPFTableCell cell = row.getCell(col);
                            XWPFParagraph p = cell.getParagraphArray(0);
                            p.setAlignment(ParagraphAlignment.CENTER);
                            XWPFRun r = p.createRun();
                            r.setFontSize(12);
                            r.setText(String.valueOf(item.get("ageDay")));
                            break;
                        }
                        case 1: {
                            XWPFTableCell cell = row.getCell(col);
                            cell.setWidth("4000");
                            XWPFParagraph p = cell.getParagraphArray(0);
                            XWPFRun r = p.createRun();
                            r.setFontSize(12);
                            if (item.get("plantHeath") != null) {
                                String plantHeath = String.valueOf(item.get("plantHeath"));
                                breakLine(cell, r, plantHeath);
                            }
                            break;
                        }
                        case 2: {
                            XWPFTableCell cell = row.getCell(col);
                            XWPFParagraph p = cell.getParagraphArray(0);
                            XWPFRun r = p.createRun();
                            r.setFontSize(12);
                            if (item.get("treatmentMethod") != null) {
                                String treatmentMethod = String.valueOf(item.get("treatmentMethod"));
                                breakLine(cell, r, treatmentMethod);
                            }
                            break;
                        }
                        case 3: {
                            XWPFTableCell cell = row.getCell(col);
                            XWPFParagraph p = cell.getParagraphArray(0);
                            XWPFRun r = p.createRun();
                            r.setFontSize(12);
                            r.setText(String.valueOf(item.get("executeBy")));
                            break;
                        }
                        case 4: {
                            XWPFTableCell cell = row.getCell(col);
                            XWPFParagraph p = cell.getParagraphArray(0);
                            XWPFRun r = p.createRun();
                            DateFormat dateFormatter = new SimpleDateFormat(EXPORT_DATE_FORMAT);

                            r.setFontSize(12);
                            if (item.get("sprayingResultAt") != null) {
                                String sprayingResultAt = String.valueOf(item.get("sprayingResultAt"));
                                String[] lines = sprayingResultAt.split("\n");
                                for (int i = 0; i < lines.length; i++) {
                                    String[] innerLines = lines[i].split(": ");
                                    String sprayingResultDate = "";
                                    try {
                                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
                                        if(innerLines[0].contains("Ngày ghi nhận kết quả")){
                                            sprayingResultDate = innerLines[0] + ": " + dateFormatter.format(formatter.parse(innerLines[1]));
                                        }
                                        else {
                                            sprayingResultDate = lines[i];
                                        }
                                    } catch (DateTimeParseException ex) {
                                        // ignore exception
                                    } catch (ParseException e) {
                                        // ignore exception
                                    }

                                    if (i == 0) {
                                        r.setText(sprayingResultDate);
                                    } else {
                                        XWPFParagraph paragraph = cell.addParagraph();
                                        XWPFRun run = paragraph.createRun();
                                        run.setFontSize(12);
                                        run.setText(sprayingResultDate);
                                    }
                                }
                            }
                            break;
                        }
                    }

                }

            }
        }


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

    private void breakLine(XWPFTableCell cell, XWPFRun r, String sourceText) {
        String[] lines = sourceText.split("\n");
        for (int i = 0; i < lines.length; i++) {
            if (i == 0) {
                r.setText(lines[i]);
            } else {
                XWPFParagraph paragraph = cell.addParagraph();
                XWPFRun run = paragraph.createRun();
                run.setFontSize(12);
                run.setText(lines[i]);
            }
        }
    }


}
