package de.avatic.lcc.service.api;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.avatic.lcc.dto.custom.CustomDTO;
import de.avatic.lcc.dto.generic.PropertyDTO;
import de.avatic.lcc.model.db.properties.SystemPropertyMappingId;
import de.avatic.lcc.model.taric.AppliedMeasure;
import de.avatic.lcc.model.taric.Nomenclature;
import de.avatic.lcc.repositories.country.CountryRepository;
import de.avatic.lcc.repositories.properties.PropertyRepository;
import de.avatic.lcc.util.exception.base.InternalErrorException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class CustomApiService {

    private final CountryRepository countryRepository;
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    private final String taricApiUrl;
    private final String taricApiKey;
    private final PropertyRepository propertyRepository;

    public CustomApiService(@Value("${taric.api.key:}") String taricApiKey, @Value("${taric.api.url:}") String taricApiUrl, CountryRepository countryRepository, RestTemplate restTemplate, ObjectMapper objectMapper, PropertyRepository propertyRepository) {
        this.taricApiKey = taricApiKey;
        this.taricApiUrl = taricApiUrl;
        this.countryRepository = countryRepository;
        this.restTemplate = restTemplate;
        this.objectMapper = objectMapper;
        this.propertyRepository = propertyRepository;
    }

    public CustomDTO getTariffRate(String hsCode, Integer countryId) {
        var country = countryRepository.getById(countryId);
        String iso = country.orElseThrow().getIsoCode().name();

        try {
            URI uri = UriComponentsBuilder.fromUriString(taricApiUrl).queryParam("hscode", hsCode).queryParam("countryCode", iso).path("/api/v1/tariff").build().toUri();
            ResponseEntity<Map<String, Map<String, List<AppliedMeasure>>>> response = restTemplate.exchange(uri, HttpMethod.GET, createHttpEntity(), new ParameterizedTypeReference<>() {
            });

            var applMeasure = response.getBody();

            if (applMeasure != null) {
                List<String> sortedKeys = applMeasure.keySet().stream().sorted().toList().reversed();

                for (String key : sortedKeys) {
                    Map<String, List<AppliedMeasure>> geo = applMeasure.get(key);

                    if (geo.containsKey(iso)) {
                        var rate = findMeasure(geo.get(iso));
                        if (rate.isPresent()) return new CustomDTO(false, rate.get(), null, countryId);
                    }

                    for (String geoKey : geo.keySet()) {
                        if (!"ERGA OMNES".equals(geoKey)) {
                            var rate = findMeasure(geo.get(geoKey));
                            if (rate.isPresent()) return new CustomDTO(false, rate.get(), null, countryId);
                        }
                    }

                    for (String geoKey : geo.keySet()) {
                        if ("ERGA OMNES".equals(geoKey)) {
                            var rate = findMeasure(geo.get(geoKey));
                            if (rate.isPresent()) return new CustomDTO(false, rate.get(), null, countryId);
                        }
                    }
                }
            }


        } catch (Exception _) {

        }

        return propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.TARIFF_RATE).map(PropertyDTO::getCurrentValue).map(Double::valueOf).map(aDouble -> new CustomDTO(aDouble, countryId)).orElseThrow(() -> new InternalErrorException("Unable to load default custom rate. Please contact support."));
    }

    private Optional<Double> findMeasure(List<AppliedMeasure> appliedMeasures) {

        var series = List.of("B", "C", "D");

        for (var seriesCode : series) {

            var measures = appliedMeasures.stream().filter(m -> m.getMeasure().getMeasureSeries().getMeasureSeriesCode().equals(seriesCode))
                    .map(AppliedMeasure::getAmount).filter(str -> str != null && str.trim().matches("\\d+\\.\\d+\\s*%"))
                    .map(str -> Double.parseDouble(str.trim().replace("%", "").trim()) / 100)
                    .toList();

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

        return Optional.empty();

    }

    private HttpHeaders createHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.set("X-API-Key", taricApiKey);
        headers.setContentType(MediaType.APPLICATION_JSON);
        return headers;
    }

    private HttpEntity<Void> createHttpEntity() {
        return new HttpEntity<>(createHeaders());
    }

    public List<String> getNomenclature(String searchTerm) {
        try {

            URI uri = UriComponentsBuilder.fromUriString(taricApiUrl).path("/api/v1/nomenclature/declarable").queryParam("hscode", searchTerm).build().toUri();

            ResponseEntity<List<Nomenclature>> response = restTemplate.exchange(uri, HttpMethod.GET, createHttpEntity(), new ParameterizedTypeReference<>() {
            });

            return response.getBody().stream().map(Nomenclature::getHscode).toList();

        } catch (Exception e) {
            return List.of();
        }
    }

    public boolean validate(String hsCode) {
        try {

            URI url = UriComponentsBuilder.fromUriString(taricApiUrl).queryParam("hscode", hsCode).buildAndExpand("/api/v1/nomenclature").encode().toUri();

            HttpHeaders headers = new HttpHeaders();
            headers.set("X-API-Key", taricApiKey);
            headers.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<String> entity = new HttpEntity<>(headers);

            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);

            // Check if response is valid (200) and parse to Nomenclature
            if (response.getStatusCode() == HttpStatus.OK) {
                Nomenclature nomenclature = objectMapper.readValue(response.getBody(), Nomenclature.class);

                // Check if isLeaf is true
                return Boolean.TRUE.equals(nomenclature.getLeaf());
            }

            return false;
        } catch (HttpClientErrorException.NotFound e) {
            // 404 - HS code not found
            return false;
        } catch (Exception e) {
            return false;
        }
    }

}