package de.avatic.taric.service;


import de.avatic.taric.error.ArgumentException;
import de.avatic.taric.model.AppliedMeasure;
import de.avatic.taric.model.Geo;
import de.avatic.taric.model.GeoGroup;
import de.avatic.taric.model.Nomenclature;
import de.avatic.taric.repository.ImportRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TariffService {

    private final GeoService geoService;
    private final NomenclatureService nomenclatureService;
    private final ImportRepository importRepository;

    public TariffService(GeoService geoService, NomenclatureService nomenclatureService, ImportRepository importRepository) {
        this.geoService = geoService;
        this.nomenclatureService = nomenclatureService;
        this.importRepository = importRepository;
    }

    public Optional<Double> importTariffs(String hsCode, String countryCode) {

        var geoGroups = geoService.getGeoGroupByCountryCode(countryCode);
        var geo = geoService.getGeo(countryCode);

        var nomenclature = nomenclatureService.getNomenclature(hsCode);
        var cascade = nomenclatureService.getNomenclatureCascade(hsCode);

        if (nomenclature.isEmpty() || !nomenclature.get().getIsLeaf()) throw new ArgumentException("hsCode");
        if (geo.isEmpty()) throw new ArgumentException("countryCode");


        for (Nomenclature n : cascade) {
            var applMeasures = findAppliedMeasureByGeo(n, geo.get());

            if (!applMeasures.isEmpty()) {
                var tariff = findTariff(applMeasures);

                if (tariff.isPresent())
                    return tariff;

            }

        }

        for (Nomenclature n : cascade) {
            var applMeasures = findAppliedMeasureByGeoGroup(n, geo.get(), geoGroups);

            if (!applMeasures.isEmpty()) {
                var tariff = findTariff(applMeasures);

                if (tariff.isPresent())
                    return tariff;
            }
        }

        return Optional.empty();

    }

    private Optional<Double> findTariff(Collection<AppliedMeasure> measures) {

        List<Double> percentages = measures.stream().map(AppliedMeasure::getAmount)
                .filter(str -> str != null && str.trim().matches("\\d+\\.\\d+\\s*%"))
                .map(str -> Double.parseDouble(str.trim().replace("%", "").trim())/100)
                .toList();

        if (!percentages.isEmpty()) return Optional.of(percentages.getFirst());
        return Optional.empty();
    }

    public Collection<AppliedMeasure> findAppliedMeasureByGeo(Nomenclature nomenclature, Geo geo) {

        var foundImport = importRepository.findByNomenclatureAndGeo(nomenclature.getId(), geo.getId());
        var applMeasures = new ArrayList<AppliedMeasure>();

        if (foundImport.isEmpty()) return Collections.emptyList();

        for (var imp : foundImport) {
            for (var applMeasure : imp.getAppliedMeasures()) {
                if (applMeasure.getExclusions().stream().noneMatch(ex -> ex.getGeo().getId().equals(geo.getId()))) {
                    applMeasures.add(applMeasure);
                }
            }
        }

        return applMeasures;

    }

    public Collection<AppliedMeasure> findAppliedMeasureByGeoGroup(Nomenclature nomenclature, Geo geo, List<GeoGroup> geoGroups) {
        var foundImport = importRepository.findByNomenclatureAndGeoGroupIn(nomenclature.getId(), geoGroups.stream().map(GeoGroup::getId).toList());
        var applMeasures = new ArrayList<AppliedMeasure>();

        if (foundImport.isEmpty()) return Collections.emptyList();

        for (var imp : foundImport) {
            for (var applMeasure : imp.getAppliedMeasures()) {
                if (applMeasure.getExclusions().stream().noneMatch(ex -> ex.getGeo().getId().equals(geo.getId()))) {
                    applMeasures.add(applMeasure);
                }
            }
        }

        return applMeasures;
    }


}
