package danielcortes.xyz.informes;

import danielcortes.xyz.data.Configuration;
import danielcortes.xyz.data.DAOManager;
import danielcortes.xyz.models.estado_resultado.EstadoResultado;
import danielcortes.xyz.models.tipo_egreso.TipoEgreso;
import danielcortes.xyz.utils.StringUtils;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderExtent;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.PropertyTemplate;

public class InformeEstadoResultado {

  private ArrayList<Row> titles;
  private ArrayList<Row> headers;
  private ArrayList<Row> footers;
  private ArrayList<Row> ventaRows;
  private ArrayList<Row> gastosOperacionalesRows;
  private ArrayList<Row> serviciosRows;
  private ArrayList<Row> gastosGeneralesRows;
  private ArrayList<Row> resumenGeneralRows;

  private YearMonth mes;

  private EstadoResultado estadoResultado;

  private Workbook wb;
  private Sheet sheet;
  private CreationHelper createHelper;
  private HashMap<String, CellStyle> styles;

  public InformeEstadoResultado(YearMonth mes) {
    this.mes = mes;

    this.estadoResultado = DAOManager.getEstadoResultadoDAO().getByMonth(this.mes).orElse(EstadoResultado.EMPTY);

    this.wb = new HSSFWorkbook();
    this.sheet = wb.createSheet();
    this.createHelper = wb.getCreationHelper();

    this.titles = new ArrayList<>();
    this.headers = new ArrayList<>();
    this.footers = new ArrayList<>();
    this.ventaRows = new ArrayList<>();
    this.gastosOperacionalesRows = new ArrayList<>();
    this.serviciosRows = new ArrayList<>();
    this.gastosGeneralesRows = new ArrayList<>();
    this.resumenGeneralRows = new ArrayList<>();

    this.styles = this.generateStyles();
  }

  private void fillTitle() {
    int startRow = 0;
    Row titleMes = this.sheet.createRow(startRow++);
    Row titleLocal = this.sheet.createRow(startRow++);

    this.titles.add(titleMes);
    this.titles.add(titleLocal);

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM yyyy");
    String formatedMes = this.mes.format(formatter);

    titleMes.createCell(0).setCellValue("Estado Resultado");
    titleMes.createCell(1).setCellValue(StringUtils.capitalize(formatedMes));

    titleLocal.createCell(0).setCellValue("Local");
    titleLocal.createCell(1).setCellValue(StringUtils.capitalize(Configuration.get("nombre_caja")));
  }

  private void fillVentaData() {
    int startId = 2;

    Row header = this.sheet.createRow(startId++);
    Row bruto = sheet.createRow(startId++);
    Row neto = sheet.createRow(startId++);
    Row iva = sheet.createRow(startId++);
    Row exentas = sheet.createRow(startId++);
    Row footer = sheet.createRow(startId++);

    this.headers.add(header);
    this.ventaRows.add(bruto);
    this.ventaRows.add(neto);
    this.ventaRows.add(iva);
    this.ventaRows.add(exentas);
    this.footers.add(footer);

    header.createCell(0).setCellValue("Venta");
    header.createCell(1);

    bruto.createCell(0).setCellValue("Bruto");
    bruto.createCell(1).setCellValue(DAOManager.getIngresoDAO().getTotalIngresoMes(this.mes));

    neto.createCell(0).setCellValue("Neto");
    neto.createCell(1).setCellFormula("B4/1.19");

    iva.createCell(0).setCellValue("IVA");
    iva.createCell(1).setCellFormula("B4-B5");

    exentas.createCell(0).setCellValue("Exentas");
    exentas.createCell(1).setCellValue(DAOManager.getIngresoDAO().getTotalExentasMes(this.mes));

    footer.createCell(0).setCellValue("Neto mas Exentas");
    footer.createCell(1).setCellFormula("B7+B5");
  }

  private void fillGastosOperacionalesData() {
    int startId = 9;
    Row header = sheet.createRow(startId++);
    Row costoVenta = sheet.createRow(startId++);
    Row porcentajeCostoVenta = sheet.createRow(startId++);
    Row remuneraciones = sheet.createRow(startId++);
    Row finiquitos = sheet.createRow(startId++);
    Row aguinaldo = sheet.createRow(startId++);
    Row partime = sheet.createRow(startId++);
    Row bonosPersonal = sheet.createRow(startId++);
    Row honorariosContador = sheet.createRow(startId++);
    Row arriendo = sheet.createRow(startId++);
    Row footer = sheet.createRow(startId++);

    this.headers.add(header);
    this.gastosOperacionalesRows.add(costoVenta);
    this.gastosOperacionalesRows.add(porcentajeCostoVenta);
    this.gastosOperacionalesRows.add(remuneraciones);
    this.gastosOperacionalesRows.add(finiquitos);
    this.gastosOperacionalesRows.add(aguinaldo);
    this.gastosOperacionalesRows.add(partime);
    this.gastosOperacionalesRows.add(bonosPersonal);
    this.gastosOperacionalesRows.add(honorariosContador);
    this.gastosOperacionalesRows.add(arriendo);
    this.footers.add(footer);

    header.createCell(0).setCellValue("Gastos Operacionales");
    header.createCell(1);

    costoVenta.createCell(0).setCellValue("Costo de Venta");
    costoVenta.createCell(1).setCellValue(this.estadoResultado.getCostoVenta());

    porcentajeCostoVenta.createCell(0).setCellValue("Porcentaje Costo de Venta");
    porcentajeCostoVenta.createCell(1).setCellFormula("B11/B4");

    remuneraciones.createCell(0).setCellValue("Remuneraciones");
    remuneraciones.createCell(1).setCellValue(this.estadoResultado.getRemuneraciones());

    finiquitos.createCell(0).setCellValue("Finiquitos");
    finiquitos.createCell(1).setCellValue(this.estadoResultado.getFiniquitos());

    aguinaldo.createCell(0).setCellValue("Aguinaldo");
    aguinaldo.createCell(1).setCellValue(this.estadoResultado.getAguinaldo());

    partime.createCell(0).setCellValue("Partime");
    partime.createCell(1).setCellValue(DAOManager.getEgresoDAO().getTotalEgresoMesPorTipo(this.mes,
        DAOManager.getTipoEgresoDAO().getByNombre("Pago Partime").orElse(TipoEgreso.EMPTY)));

    bonosPersonal.createCell(0).setCellValue("Bonos Personal");
    bonosPersonal.createCell(1).setCellValue(this.estadoResultado.getBonosPersonal());

    honorariosContador.createCell(0).setCellValue("Honorarios Contador");
    honorariosContador.createCell(1).setCellValue(this.estadoResultado.getHonorariosContador());

    arriendo.createCell(0).setCellValue("Arriendo");
    arriendo.createCell(1).setCellValue(this.estadoResultado.getArriendo());

    footer.createCell(0).setCellValue("Total");
    footer.createCell(1).setCellFormula("Sum(B11:B19)");
  }

  private void fillServiciosData() {
    int startId = 21;
    Row header = sheet.createRow(startId++);
    Row agua = sheet.createRow(startId++);
    Row luz = sheet.createRow(startId++);
    Row gas = sheet.createRow(startId++);
    Row telefono = sheet.createRow(startId++);
    Row otro = sheet.createRow(startId++);
    Row footer = sheet.createRow(startId++);

    this.headers.add(header);
    this.serviciosRows.add(agua);
    this.serviciosRows.add(luz);
    this.serviciosRows.add(gas);
    this.serviciosRows.add(telefono);
    this.serviciosRows.add(otro);
    this.footers.add(footer);

    header.createCell(0).setCellValue("Servicios");
    header.createCell(1);
    agua.createCell(0).setCellValue("Agua");
    agua.createCell(1).setCellValue(this.estadoResultado.getAgua());
    luz.createCell(0).setCellValue("Luz");
    luz.createCell(1).setCellValue(this.estadoResultado.getLuz());
    gas.createCell(0).setCellValue("Gas");
    gas.createCell(1).setCellValue(this.estadoResultado.getGas());
    telefono.createCell(0).setCellValue("Telefono");
    telefono.createCell(1).setCellValue(this.estadoResultado.getTelefono());
    otro.createCell(0).setCellValue("Otros");
    otro.createCell(1).setCellValue(this.estadoResultado.getOtroServicio());
    footer.createCell(0).setCellValue("Total");
    footer.createCell(1).setCellFormula("Sum(B23:B27)");
  }

  private void fillGastosGeneralesData() {
    TipoEgreso facturaGastosGenerales = DAOManager.getTipoEgresoDAO()
        .getByNombre("Factura Gastos Generales").orElse(TipoEgreso.EMPTY);
    TipoEgreso gastoGeneralConBoleta = DAOManager.getTipoEgresoDAO()
        .getByNombre("Gasto General Con Boleta").orElse(TipoEgreso.EMPTY);
    TipoEgreso gastoGeneralSinRespaldo = DAOManager.getTipoEgresoDAO()
        .getByNombre("Gasto General Sin Respaldo").orElse(TipoEgreso.EMPTY);

    int startId = 29;

    Row header = sheet.createRow(startId++);
    Row cuentaCorrienteFactura = sheet.createRow(startId++);
    Row cuentaCorrienteBoleta = sheet.createRow(startId++);
    Row cuentaCorrienteSinRespaldo = sheet.createRow(startId++);
    Row efectivoFactura = sheet.createRow(startId++);
    Row efectivoBoleta = sheet.createRow(startId++);
    Row efectivoSinRespaldo = sheet.createRow(startId++);
    Row footer = sheet.createRow(startId++);

    this.headers.add(header);
    this.gastosGeneralesRows.add(cuentaCorrienteFactura);
    this.gastosGeneralesRows.add(cuentaCorrienteBoleta);
    this.gastosGeneralesRows.add(cuentaCorrienteSinRespaldo);
    this.gastosGeneralesRows.add(efectivoFactura);
    this.gastosGeneralesRows.add(efectivoBoleta);
    this.gastosGeneralesRows.add(efectivoSinRespaldo);
    this.footers.add(footer);

    header.createCell(0).setCellValue("Gastos Generales");
    header.createCell(1);
    cuentaCorrienteFactura.createCell(0).setCellValue("CTA CTE con Factura");
    cuentaCorrienteFactura.createCell(1)
        .setCellValue(this.estadoResultado.getCuentaCorrienteFactura());
    cuentaCorrienteBoleta.createCell(0).setCellValue("CTA CTE con Boleta");
    cuentaCorrienteBoleta.createCell(1)
        .setCellValue(this.estadoResultado.getCuentaCorrienteBoleta());
    cuentaCorrienteSinRespaldo.createCell(0).setCellValue("CTA CTE sin Respaldo");
    cuentaCorrienteSinRespaldo.createCell(1)
        .setCellValue(this.estadoResultado.getCuentaCorrienteSinRespaldo());
    efectivoFactura.createCell(0).setCellValue("Efectivo con Factura");
    efectivoFactura.createCell(1).setCellValue(
        DAOManager.getEgresoDAO().getTotalEgresoMesPorTipo(this.mes, facturaGastosGenerales));
    efectivoBoleta.createCell(0).setCellValue("Efectivo con Boleta");
    efectivoBoleta.createCell(1).setCellValue(
        DAOManager.getEgresoDAO().getTotalEgresoMesPorTipo(this.mes, gastoGeneralConBoleta));
    efectivoSinRespaldo.createCell(0).setCellValue("Efectivo sin Respaldo");
    efectivoSinRespaldo.createCell(1).setCellValue(
        DAOManager.getEgresoDAO().getTotalEgresoMesPorTipo(this.mes, gastoGeneralSinRespaldo));
    footer.createCell(0).setCellValue("Total");
    footer.createCell(1).setCellFormula("Sum(B31:B36)");
  }

  private void fillResumenGeneralData() {
    int startId = 38;

    Row header = sheet.createRow(startId++);
    Row utilidad = sheet.createRow(startId++);
    Row porcentajePPM = sheet.createRow(startId++);
    Row ppmMes = sheet.createRow(startId++);
    Row ivaMes = sheet.createRow(startId++);
    Row ivaMesPpm = sheet.createRow(startId++);
    Row ivaFavor = sheet.createRow(startId++);
    Row aPagar = sheet.createRow(startId++);
    Row footer = sheet.createRow(startId++);

    this.headers.add(header);
    this.resumenGeneralRows.add(utilidad);
    this.resumenGeneralRows.add(porcentajePPM);
    this.resumenGeneralRows.add(ppmMes);
    this.resumenGeneralRows.add(ivaMes);
    this.resumenGeneralRows.add(ivaMesPpm);
    this.resumenGeneralRows.add(ivaFavor);
    this.resumenGeneralRows.add(aPagar);
    this.footers.add(footer);

    header.createCell(0).setCellValue("Resumen General");
    header.createCell(1);

    utilidad.createCell(0).setCellValue("Utilidad");
    utilidad.createCell(1).setCellFormula("B4 - B20 - B28 - B37");

    porcentajePPM.createCell(0).setCellValue("Porcentaje PPM");
    porcentajePPM.createCell(1).setCellValue(this.estadoResultado.getPpm() / 100);

    ppmMes.createCell(0).setCellValue("PPM Mes");
    ppmMes.createCell(1).setCellFormula("B8 * B41");

    ivaMes.createCell(0).setCellValue("+ IVA Mes");
    ivaMes.createCell(1).setCellFormula("B6");

    ivaMesPpm.createCell(0).setCellValue("Total PPM + IVA");
    ivaMesPpm.createCell(1).setCellFormula("B42 + B43");

    ivaFavor.createCell(0).setCellValue("- IVA a Favor");
    ivaFavor.createCell(1).setCellValue(this.estadoResultado.getIvaFavor());

    aPagar.createCell(0).setCellValue("A Pagar PPM + IVA");
    aPagar.createCell(1).setCellFormula("B44 - B45");

    footer.createCell(0).setCellValue("Resultado");
    footer.createCell(1).setCellFormula("B40 - B46");
  }

  private void setStyles() {
    for (Row title : this.titles) {
      title.getCell(0).setCellStyle(this.styles.get("title"));
      title.getCell(1).setCellStyle(this.styles.get("title"));
      title.setHeightInPoints(30);
    }

    for (Row header : this.headers) {
      header.getCell(0).setCellStyle(this.styles.get("header"));
      header.getCell(1).setCellStyle(this.styles.get("header"));
      header.setHeightInPoints(20);
    }

    for (Row footer : this.footers) {
      footer.getCell(0).setCellStyle(this.styles.get("footer"));
      footer.getCell(1).setCellStyle(this.styles.get("footer_money"));
      footer.setHeightInPoints(20);
    }

    for (Row row : this.ventaRows) {
      row.getCell(0).setCellStyle(this.styles.get("regular"));
      row.getCell(1).setCellStyle(this.styles.get("money"));
      row.setHeightInPoints(15);
    }
    for (Row row : this.gastosOperacionalesRows) {
      row.getCell(0).setCellStyle(this.styles.get("regular"));
      row.getCell(1).setCellStyle(this.styles.get("money"));
      row.setHeightInPoints(15);
    }
    for (Row row : this.serviciosRows) {
      row.getCell(0).setCellStyle(this.styles.get("regular"));
      row.getCell(1).setCellStyle(this.styles.get("money"));
      row.setHeightInPoints(15);
    }
    for (Row row : this.gastosGeneralesRows) {
      row.getCell(0).setCellStyle(this.styles.get("regular"));
      row.getCell(1).setCellStyle(this.styles.get("money"));
      row.setHeightInPoints(15);
    }
    for (Row row : this.resumenGeneralRows) {
      row.getCell(0).setCellStyle(this.styles.get("regular"));
      row.getCell(1).setCellStyle(this.styles.get("money"));
      row.setHeightInPoints(15);
    }

    //La fila de porcentaje costo de venta tiene que tener formato de porcentaje
    this.gastosOperacionalesRows.get(1).getCell(1).setCellStyle(this.styles.get("percent"));

    //La fila de ppm mes en el resumen general tiene un campo distinto que contiene un porcentaje
    resumenGeneralRows.get(1).getCell(1).setCellStyle(this.styles.get("percent"));

    sheet.autoSizeColumn(0);
    sheet.autoSizeColumn(1);
    sheet.autoSizeColumn(2);
  }

  private void addBorders() {
    PropertyTemplate pt = new PropertyTemplate();
    for (Row header : headers) {
      int rowNum = header.getRowNum();
      CellRangeAddress range = new CellRangeAddress(rowNum, rowNum, 0, 1);
      pt.drawBorders(range, BorderStyle.THIN, BorderExtent.OUTSIDE);
    }

    for (Row footer : footers) {
      int rowNum = footer.getRowNum();
      CellRangeAddress range = new CellRangeAddress(rowNum, rowNum, 0, 1);
      pt.drawBorders(range, BorderStyle.THIN, BorderExtent.OUTSIDE);
    }

    int ventaStart = ventaRows.get(0).getRowNum();
    int ventaEnd = ventaRows.get(ventaRows.size() - 1).getRowNum();
    pt.drawBorders(new CellRangeAddress(ventaStart, ventaEnd, 0, 1), BorderStyle.THIN,
        BorderExtent.VERTICAL);

    int gastosOperacionalesStart = gastosOperacionalesRows.get(0).getRowNum();
    int gastosOperacionalesEnd = gastosOperacionalesRows.get(gastosOperacionalesRows.size() - 1)
        .getRowNum();
    pt.drawBorders(new CellRangeAddress(gastosOperacionalesStart, gastosOperacionalesEnd, 0, 1),
        BorderStyle.THIN, BorderExtent.VERTICAL);

    int serviciosStart = serviciosRows.get(0).getRowNum();
    int serviciosEnd = serviciosRows.get(serviciosRows.size() - 1).getRowNum();
    pt.drawBorders(new CellRangeAddress(serviciosStart, serviciosEnd, 0, 1), BorderStyle.THIN,
        BorderExtent.VERTICAL);

    int gastosGeneralesStart = gastosGeneralesRows.get(0).getRowNum();
    int gastosGeneralesEnd = gastosGeneralesRows.get(gastosGeneralesRows.size() - 1).getRowNum();
    pt.drawBorders(new CellRangeAddress(gastosGeneralesStart, gastosGeneralesEnd, 0, 1),
        BorderStyle.THIN, BorderExtent.VERTICAL);

    int resumenStart = resumenGeneralRows.get(0).getRowNum();
    int resumenEnd = resumenGeneralRows.get(resumenGeneralRows.size() - 1).getRowNum();
    pt.drawBorders(new CellRangeAddress(resumenStart, resumenEnd, 0, 1), BorderStyle.THIN,
        BorderExtent.VERTICAL);

    pt.applyBorders(this.sheet);
  }

  public Workbook generarInforme() {
    fillTitle();
    fillVentaData();
    fillGastosOperacionalesData();
    fillServiciosData();
    fillGastosGeneralesData();
    fillResumenGeneralData();
    setStyles();
    addBorders();

    return wb;
  }

  private HashMap<String, CellStyle> generateStyles() {
    Font font = this.wb.createFont();
    font.setBold(true);
    font.setColor(IndexedColors.WHITE.getIndex());

    Font titleFont = this.wb.createFont();
    titleFont.setBold(true);
    titleFont.setFontHeightInPoints((short) 15);

    CellStyle regularStyle = this.wb.createCellStyle();

    CellStyle grayStyle = this.wb.createCellStyle();
    grayStyle.setFont(font);
    grayStyle.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
    grayStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

    CellStyle moneyStyle = this.wb.createCellStyle();
    moneyStyle
        .setDataFormat(this.createHelper.createDataFormat().getFormat("\"$\"#,##0_);(\"$\"#,##0)"));

    CellStyle percentStyle = this.wb.createCellStyle();
    percentStyle.setDataFormat(this.createHelper.createDataFormat().getFormat("0.00%"));

    CellStyle titleStyle = this.wb.createCellStyle();
    titleStyle.setFont(titleFont);

    CellStyle headerStyle = this.wb.createCellStyle();
    headerStyle.cloneStyleFrom(grayStyle);
    headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);

    CellStyle footerStyle = this.wb.createCellStyle();
    footerStyle.cloneStyleFrom(grayStyle);
    footerStyle.setVerticalAlignment(VerticalAlignment.CENTER);

    CellStyle footerMoneyStyle = this.wb.createCellStyle();
    footerMoneyStyle.cloneStyleFrom(footerStyle);
    footerMoneyStyle
        .setDataFormat(this.createHelper.createDataFormat().getFormat("\"$\"#,##0_);(\"$\"#,##0)"));

    HashMap<String, CellStyle> styles = new HashMap<>();
    styles.put("regular", regularStyle);
    styles.put("title", titleStyle);
    styles.put("money", moneyStyle);
    styles.put("percent", percentStyle);
    styles.put("header", headerStyle);
    styles.put("footer", footerStyle);
    styles.put("footer_money", footerMoneyStyle);

    return styles;
  }

}
