package com.oguerreiro.resilient.service.query;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.oguerreiro.resilient.domain.EmissionFactor;
import com.oguerreiro.resilient.repository.EmissionFactorRepository;
import com.oguerreiro.resilient.service.AbstractResilientService;
import com.oguerreiro.resilient.service.dto.EmissionFactorDTO;
import com.oguerreiro.resilient.service.mapper.EmissionFactorMapper;

/**
 * Service Implementation for managing {@link com.oguerreiro.resilient.domain.EmissionFactor}.
 */
@Service
@Transactional
public class EmissionFactorService extends AbstractResilientService<EmissionFactorDTO, EmissionFactor, Long> {
  private final EmissionFactorRepository emissionFactorRepository;
  private final EmissionFactorMapper emissionFactorMapper;

  public EmissionFactorService(EmissionFactorRepository emissionFactorRepository,
      EmissionFactorMapper emissionFactorMapper) {
    super(EmissionFactor.class, emissionFactorRepository, emissionFactorMapper);
    this.emissionFactorRepository = emissionFactorRepository;
    this.emissionFactorMapper = emissionFactorMapper;
  }

  /**
   * Get one emissionFactor by id.
   *
   * @param  id the id of the entity.
   * @return    the entity.
   */
  @Override
  @Transactional(readOnly = true)
  public Optional<EmissionFactorDTO> findOne(Long id) {
    log.debug("[OVERRIDE] Request to findOne : {}", id);
    return findOneWithEagerRelationships(id);
  }

  @Transactional(readOnly = true)
  public byte[] export(Integer year, Long scopeId, Long categoryId) {
    //Build a Workbook
    try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
      Sheet sheet = workbook.createSheet("Export");

      // Header row
      int c = 0;
      Row headerRow = sheet.createRow(0);
      headerRow.createCell(c++).setCellValue("Ano");
      headerRow.createCell(c++).setCellValue("Âmbito");
      headerRow.createCell(c++).setCellValue("Categpria");
      headerRow.createCell(c++).setCellValue("Nome");
      headerRow.createCell(c++).setCellValue("Valor");
      headerRow.createCell(c++).setCellValue("Unidade");
      headerRow.createCell(c++).setCellValue("Fonte");

      // Get data with a stream (query cursor)
      try (Stream<EmissionFactor> stream = this.emissionFactorRepository.streamAll(year, scopeId, categoryId)) {
        // Loop data
        stream.forEach(emissionFactor -> {
          int cc = 0;
          // Write row
          Row row = sheet.createRow(sheet.getLastRowNum() + 1);
          row.createCell(cc++).setCellValue(emissionFactor.getYear().toString());
          row.createCell(cc++).setCellValue(emissionFactor.getVariableScope().getName());
          row.createCell(cc++).setCellValue(emissionFactor.getVariableCategory().getName());
          row.createCell(cc++).setCellValue(emissionFactor.getName());
          row.createCell(cc++).setCellValue(emissionFactor.getValue().setScale(8).toPlainString());
          row.createCell(cc++).setCellValue(emissionFactor.getUnit());
          row.createCell(cc++).setCellValue(emissionFactor.getSource());
        });
      }

      workbook.write(out);
      return out.toByteArray();
    } catch (IOException e) {
      throw new RuntimeException("Failed to generate Excel file", e);
    }
  }

  /**
   * Get all EmissionFactor wrapped in a EmissionFactorDTO
   *
   * @return the list of entities.
   */
  @Transactional(readOnly = true)
  public List<EmissionFactorDTO> findAllBySearch(Integer year, Long scopeId, Long categoryId) {
    log.debug("Request to get all EmissionFactor's.");

    return emissionFactorRepository.findAllBySearch(year, scopeId, categoryId).stream().map(
        this.emissionFactorMapper::toDto).collect(Collectors.toCollection(LinkedList::new));
  }
}
