package com.oguerreiro.resilient.service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
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.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.oguerreiro.resilient.domain.Organization;
import com.oguerreiro.resilient.domain.OutputData;
import com.oguerreiro.resilient.domain.PeriodVersion;
import com.oguerreiro.resilient.repository.OutputDataRepository;
import com.oguerreiro.resilient.service.dto.OutputDataDTO;
import com.oguerreiro.resilient.service.mapper.OutputDataMapper;

/**
 * Service Implementation for managing {@link com.oguerreiro.resilient.domain.OutputData}.
 */
@Service
@Transactional
public class OutputDataService extends AbstractResilientService<OutputDataDTO, OutputData, Long> {

  private final OutputDataRepository outputDataRepository;

  private final OutputDataMapper outputDataMapper;

  public OutputDataService(OutputDataRepository outputDataRepository, OutputDataMapper outputDataMapper) {
    super(OutputData.class, outputDataRepository, outputDataMapper);
    this.outputDataRepository = outputDataRepository;
    this.outputDataMapper = outputDataMapper;
  }

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void deleteAllByPeriodVersionInNewTransaction(PeriodVersion periodVersion) {
    // Clear logs for InputdataUpload entity
    outputDataRepository.deleteAllByPeriodVersion(periodVersion);
  }

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void saveInNewTransaction(OutputData outputData) {
    outputDataRepository.save(outputData);
  }

  public BigDecimal sumAllOutputsFor(Organization organization, PeriodVersion periodVersion, String variableCode) {
    return this.outputDataRepository.sumAllOutputsFor(organization, periodVersion, variableCode);
  }

  @Transactional(readOnly = true)
  public byte[] export(Long periodVersionId) {
    //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("Periodo");
      headerRow.createCell(c++).setCellValue("UO");
      headerRow.createCell(c++).setCellValue("Variável");
      headerRow.createCell(c++).setCellValue("");
      headerRow.createCell(c++).setCellValue("Valor");
      headerRow.createCell(c++).setCellValue("Unidade");

      // Get data with a stream (query cursor)
      try (Stream<OutputData> stream = this.outputDataRepository.streamAll(periodVersionId)) {
        // Loop data
        stream.forEach(outputData -> {
          int cc = 0;
          // Write row
          Row row = sheet.createRow(sheet.getLastRowNum() + 1);
          row.createCell(cc++).setCellValue(outputData.getPeriod().getName());
          row.createCell(cc++).setCellValue(outputData.getOwner().getCode());
          row.createCell(cc++).setCellValue(outputData.getVariable().getCode());
          row.createCell(cc++).setCellValue(outputData.getVariable().getName());
          row.createCell(cc++).setCellValue(outputData.getValue().setScale(8).toPlainString());
          row.createCell(cc++).setCellValue(outputData.getBaseUnit().getSymbol());
        });
      }

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