package de.avatic.taricdb.helper.workbook.converters;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

import org.springframework.data.jdbc.core.mapping.AggregateReference;

import de.avatic.taricdb.helper.workbook.ExcelTable;
import de.avatic.taricdb.helper.workbook.wrapper.AppliedMeasureConditionWrapper;
import de.avatic.taricdb.model.AppliedMeasureCondition;
import de.avatic.taricdb.model.Certificate;
import de.avatic.taricdb.model.ConditionType;
import de.avatic.taricdb.model.MeasureAction;
import de.avatic.taricdb.model.MonetaryUnit;
import de.avatic.taricdb.model.Unit;
import de.avatic.taricdb.service.ReferenceStorageService;

public class AppliedMeasureConditionConverter implements ExcelTableConverter<AppliedMeasureConditionWrapper> {

	private ReferenceStorageService referenceStorage;

	public AppliedMeasureConditionConverter(ReferenceStorageService referenceStorage) {
		this.referenceStorage = referenceStorage;
	}

	@Override
	public Collection<AppliedMeasureConditionWrapper> convert(ExcelTable table) {
		Collection<AppliedMeasureConditionWrapper> conditions = new ArrayList<>();
		Collection<ConditionType> conditionTypes = referenceStorage.getEntites(ConditionType.class);
		Collection<Certificate> certificates = referenceStorage.getEntites(Certificate.class);
		Collection<MonetaryUnit> monetaryUnits = referenceStorage.getEntites(MonetaryUnit.class);
		Collection<Unit> units = referenceStorage.getEntites(Unit.class);
		Collection<MeasureAction> actions = referenceStorage.getEntites(MeasureAction.class);

		table.getRows().forEach((row) -> {
			if (row.index() == 0) {
				return;
			}

			String hsCode = row.getAs("Goods code", String.class);

            if (hsCode == null || hsCode.isEmpty()) {
                return;
            }

			String originCode = row.getAs("Origin code", String.class);
			String additionalCode = row.getAs("Add code", String.class);
			String orderNumber = row.getAs("Order No.", String.class);

			String monetaryUnitCode = row.getAs("Mon. unit", String.class);
			String unitCode = row.getAs("Meas. unit", String.class);
			String amount = row.getAs("Cond. amount", String.class);
			Integer sequenceNumber = row.getAs("Sequence", Integer.class);

            LocalDate startDate = row.getAs("Start date", LocalDate.class);
            LocalDate endDate = row.getAs("End date", LocalDate.class);

			Integer measureCode = row.getAs("Meas. type code", Integer.class);

			Certificate certificate = certificates.stream()
					.filter(c -> c.getCertificateCode().equals(row.getAs("Certificate", String.class))).findAny()
					.orElse(null);
			MeasureAction action = actions.stream()
					.filter(c -> c.getMeasureActionCode().equals(row.getAs("Meas. action", String.class))).findAny()
					.orElse(null);
			ConditionType conditionType = conditionTypes.stream()
					.filter(c -> c.getConditionTypeCode().equals(row.getAs("Meas. cond", String.class))).findAny()
					.orElse(null);

			Unit unit = null;
			if (unitCode != null) {
				unit = units.stream().filter(c -> c.getUnitCode().equals(unitCode)).findAny().orElse(null);
			}

			MonetaryUnit monetaryUnit = null;
			if (monetaryUnitCode != null) {
				monetaryUnit = monetaryUnits.stream().filter(c -> c.getMonetaryUnitCode().equals(monetaryUnitCode))
						.findAny().orElse(null);
			}

			AppliedMeasureConditionWrapper condition = conditions.stream()
					.filter(c -> c.getHsCode().equals(hsCode)
							&& c.getOriginCode().equals(originCode)
							&& Objects.equals(c.getAdditionalCode(), additionalCode)
							&& Objects.equals(c.getOrderNumber(), orderNumber)
							&& c.getAppliedMeasureCondition().getSequenceNo().equals(sequenceNumber)
							&& Objects.equals(c.getAppliedMeasureCondition().getConditionType().getId(), conditionType != null ? conditionType.getId() : null)
							&& c.getMeasureCode().equals(measureCode)
                            && Objects.equals(c.getStartDate(), startDate)
                            && Objects.equals(c.getEndDate(), endDate))
					.findAny().orElse(null);

			if (condition == null) {
				condition = new AppliedMeasureConditionWrapper(
						new AppliedMeasureCondition(action != null ? AggregateReference.to(action.getId() ): null,
								monetaryUnit == null ? null : AggregateReference.to(monetaryUnit.getId()),
								unit == null ? null : AggregateReference.to(unit.getId()),
								certificate == null ? null : AggregateReference.to(certificate.getId()),
                                conditionType != null ? AggregateReference.to( conditionType.getId() ): null, amount, sequenceNumber),
						hsCode, originCode, additionalCode, orderNumber, measureCode, startDate, endDate);
				conditions.add(condition);
			} else {
				throw new IllegalArgumentException("Duplicate applied measure condtion found: " + hsCode + " "
						+ originCode + " " + additionalCode + " " + orderNumber);
			}

		});

		return conditions;
	}

}
