package de.avatic.lcc.service.excelMapper;

import de.avatic.lcc.model.bulk.HiddenTableType;
import de.avatic.lcc.model.bulk.header.HiddenCountryHeader;
import de.avatic.lcc.model.bulk.header.MatrixRateHeader;
import de.avatic.lcc.model.db.country.IsoCode;
import de.avatic.lcc.model.db.rates.MatrixRate;
import de.avatic.lcc.repositories.country.CountryRepository;
import de.avatic.lcc.repositories.rates.MatrixRateRepository;
import de.avatic.lcc.service.bulk.helper.ConstraintGenerator;
import de.avatic.lcc.service.bulk.helper.HeaderGenerator;
import de.avatic.lcc.util.exception.internalerror.ExcelValidationError;
import org.apache.poi.ss.usermodel.*;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Service
public class MatrixRateExcelMapper {

    private final HeaderGenerator headerGenerator;
    private final MatrixRateRepository matrixRateRepository;
    private final CountryRepository countryRepository;
    private final ConstraintGenerator constraintGenerator;

    public MatrixRateExcelMapper(HeaderGenerator headerGenerator, MatrixRateRepository matrixRateRepository, CountryRepository countryRepository, ConstraintGenerator constraintGenerator) {
        this.headerGenerator = headerGenerator;
        this.matrixRateRepository = matrixRateRepository;
        this.countryRepository = countryRepository;
        this.constraintGenerator = constraintGenerator;
    }

    public void fillSheet(Sheet sheet, CellStyle headerStyle, Integer periodId) {
        headerGenerator.generateHeader(sheet, MatrixRateHeader.class, headerStyle);
        matrixRateRepository.listAllRatesByPeriodId(periodId).forEach(rate -> mapToRow(rate, sheet.createRow(sheet.getLastRowNum() + 1)));
        headerGenerator.fixWidth(sheet, MatrixRateHeader.class);
    }

    public boolean isSheet(Sheet sheet) {
        try {
            headerGenerator.validateHeader(sheet, MatrixRateHeader.class);
        } catch (ExcelValidationError e) {
            return false;
        }
        return true;
    }

    public List<MatrixRate> extractSheet(Sheet sheet) {
        headerGenerator.validateHeader(sheet, MatrixRateHeader.class);

        List<MatrixRate> rates = new ArrayList<>();

        sheet.forEach(row -> {
            if (row.getRowNum() == 0) return;
            if (!isEmpty(row))
                rates.add(mapToEntity(row));
        });

        return rates;
    }

    private boolean isEmpty(Row row) {

        for (Cell cell : row) {
            if (cell.getCellType() != CellType.BLANK) {
                return false;
            }
        }

        return true;
    }

    private MatrixRate mapToEntity(Row row) {
        MatrixRate entity = new MatrixRate();

        constraintGenerator.validateStringCell(row, MatrixRateHeader.FROM_COUNTRY.ordinal());
        constraintGenerator.validateStringCell(row, MatrixRateHeader.TO_COUNTRY.ordinal());
        constraintGenerator.validateDecimalConstraint(row, MatrixRateHeader.RATE.ordinal(), 0.0, 1000000.0);

        entity.setToCountry(countryRepository.getByIsoCode(IsoCode.valueOf(row.getCell(MatrixRateHeader.TO_COUNTRY.ordinal()).getStringCellValue())).orElseThrow().getId());
        entity.setFromCountry(countryRepository.getByIsoCode(IsoCode.valueOf(row.getCell(MatrixRateHeader.FROM_COUNTRY.ordinal()).getStringCellValue())).orElseThrow().getId());
        entity.setRate(BigDecimal.valueOf(row.getCell(MatrixRateHeader.RATE.ordinal()).getNumericCellValue()));

        return entity;
    }

    private void mapToRow(MatrixRate rate, Row row) {
        row.createCell(MatrixRateHeader.FROM_COUNTRY.ordinal()).setCellValue(countryRepository.getById(rate.getFromCountry()).orElseThrow().getIsoCode().getCode());
        row.createCell(MatrixRateHeader.TO_COUNTRY.ordinal()).setCellValue(countryRepository.getById(rate.getToCountry()).orElseThrow().getIsoCode().getCode());
        row.createCell(MatrixRateHeader.RATE.ordinal()).setCellValue(rate.getRate().doubleValue());
    }

    public void createConstraints(Workbook workbook, Sheet sheet) {
        var namedRange = constraintGenerator.createReference(workbook, HiddenCountryHeader.ISO_CODE.ordinal(), HiddenTableType.COUNTRY_HIDDEN_TABLE);

        constraintGenerator.createFormulaListConstraint(sheet, MatrixRateHeader.FROM_COUNTRY.ordinal(), namedRange.getNameName());
        constraintGenerator.createFormulaListConstraint(sheet, MatrixRateHeader.TO_COUNTRY.ordinal(), namedRange.getNameName());
        constraintGenerator.createDecimalConstraint(sheet, MatrixRateHeader.RATE.ordinal(), 0.0, 1000000.0);

    }
}
