package de.avatic.lcc.service.excelMapper;

import de.avatic.lcc.model.bulk.BulkInstruction;
import de.avatic.lcc.model.bulk.BulkInstructionType;
import de.avatic.lcc.model.bulk.header.MaterialHeader;
import de.avatic.lcc.model.db.materials.Material;
import de.avatic.lcc.repositories.MaterialRepository;
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.util.ArrayList;
import java.util.List;

@Service
public class MaterialExcelMapper {

    private final HeaderGenerator headerGenerator;
    private final MaterialRepository materialRepository;
    private final ConstraintGenerator constraintGenerator;

    public MaterialExcelMapper(HeaderGenerator headerGenerator, MaterialRepository materialRepository, ConstraintGenerator constraintGenerator) {
        this.headerGenerator = headerGenerator;
        this.materialRepository = materialRepository;
        this.constraintGenerator = constraintGenerator;
    }

    public void fillSheet(Sheet sheet, CellStyle headerStyle) {
        headerGenerator.generateHeader(sheet, MaterialHeader.class, headerStyle);
        materialRepository.listAllMaterials().forEach(material -> mapToRow(material, sheet.createRow(sheet.getLastRowNum() + 1)));
        headerGenerator.fixWidth(sheet, MaterialHeader.class);
    }

    private void mapToRow(Material material, Row row) {


        row.createCell(MaterialHeader.OPERATION.ordinal()).setCellValue(BulkInstructionType.UPDATE.name());

        row.createCell(MaterialHeader.PART_NUMBER.ordinal()).setCellValue(material.getPartNumber());
        row.createCell(MaterialHeader.DESCRIPTION.ordinal()).setCellValue(material.getName());
        row.createCell(MaterialHeader.HS_CODE.ordinal()).setCellValue(material.getHsCode());
    }

    public void createConstraints(Sheet sheet) {
        constraintGenerator.createLengthConstraint(sheet, MaterialHeader.PART_NUMBER.ordinal(), 0, 12);
        constraintGenerator.createLengthConstraint(sheet, MaterialHeader.HS_CODE.ordinal(), 0, 11);
        constraintGenerator.createLengthConstraint(sheet, MaterialHeader.DESCRIPTION.ordinal(), 1, 500);
        constraintGenerator.createEnumConstraint(sheet, MaterialHeader.OPERATION.ordinal(), BulkInstructionType.class);

        constraintGenerator.createUniqueConstraint(sheet, MaterialHeader.PART_NUMBER.ordinal());

    }

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

    public List<BulkInstruction<Material>> extractSheet(Sheet sheet) {
        headerGenerator.validateHeader(sheet, MaterialHeader.class);

        var materials = new ArrayList<BulkInstruction<Material>>();
        sheet.forEach(row -> {
            if (row.getRowNum() == 0) return;
            if (!isEmpty(row))
                materials.add(mapToEntity(row));
        });
        return materials;

    }

    private boolean isEmpty(Row row) {

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

        return true;
    }

    private BulkInstruction<Material> mapToEntity(Row row) {
        Material entity = new Material();

        constraintGenerator.validateLengthConstraint(row, MaterialHeader.PART_NUMBER.ordinal(), 0, 12);
        constraintGenerator.validateLengthConstraint(row, MaterialHeader.HS_CODE.ordinal(), 0, 11);
        constraintGenerator.validateLengthConstraint(row, MaterialHeader.DESCRIPTION.ordinal(), 1, 500);
        constraintGenerator.validateEnumConstraint(row, MaterialHeader.OPERATION.ordinal(), BulkInstructionType.class);


        entity.setPartNumber(row.getCell(MaterialHeader.PART_NUMBER.ordinal()).getStringCellValue());
        entity.setName(row.getCell(MaterialHeader.DESCRIPTION.ordinal()).getStringCellValue());
        entity.setHsCode(row.getCell(MaterialHeader.HS_CODE.ordinal()).getStringCellValue());
        entity.setNormalizedPartNumber(normalizePartNumber(entity.getPartNumber()));
        entity.setDeprecated(false);

        if (!validateHsCode(entity.getHsCode())) throw new IllegalArgumentException("Invalid HS Code");

        return new BulkInstruction<>(entity, BulkInstructionType.valueOf(row.getCell(MaterialHeader.OPERATION.ordinal()).getStringCellValue()));
    }

    private String normalizePartNumber(String partNumber) {
        if (partNumber.length() > 12) throw new IllegalArgumentException("Part number must be less than 12 characters");
        return "000000000000".concat(partNumber).substring(partNumber.length());
    }

    private boolean validateHsCode(String hsCode) {
        //TODO check via api?!
        return true;
    }

}
