package de.avatic.taric.service;


import de.avatic.taric.error.ArgumentException;
import de.avatic.taric.model.*;
import de.avatic.taric.repository.ImportRepository;
import de.avatic.taric.repository.MeasureRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Slf4j
public class TariffService {

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

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

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

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

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

        var customAppl = measureRepository.getMeasureByShortDesc("APPL");

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

        if (customAppl.isEmpty()) throw new InternalError("APPL not found");


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

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

                if (tariff.isPresent())
                    return tariff;

            }

        }

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

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

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

        return Optional.empty();

    }

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

        var code = appl.getMeasureCode();


        List<AppliedMeasure> filteredMeasures = measures.stream()
                .filter(meas -> meas.getAmount() != null && meas.getAmount().trim().matches("\\d+\\.\\d+\\s*%"))
                .toList();

        AppliedMeasure customTariff = filteredMeasures.stream().filter(meas -> meas.getMeasure().getId().equals(appl.getId())).findAny().orElse(filteredMeasures.isEmpty() ? null : filteredMeasures.getFirst());

        if (!filteredMeasures.isEmpty()) return Optional.of(customTariff).map(meas -> Double.parseDouble(meas.getAmount().trim().replace("%", "").trim())/100);
        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;
    }


}
