package ru.soune.nocopy.service.geo;

import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.model.CityResponse;
import com.maxmind.geoip2.model.CountryResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Service
@Slf4j
public class GeoCountryService {

    private final DatabaseReader countryDbReader;

    private final DatabaseReader cityDbReader;

    public GeoCountryService() throws IOException {
        Resource resource = new ClassPathResource("geo/GeoLite2-Country.mmdb");
        this.countryDbReader = new DatabaseReader.Builder(resource.getInputStream()).build();

        Resource cityResource = new ClassPathResource("geo/GeoLite2-City.mmdb");
        this.cityDbReader = new DatabaseReader.Builder(cityResource.getInputStream()).build();

    }

    public String getCountryName(String input) {
        try {
            CountryResponse response = extractCountry(input);

            return response.getCountry().getName();
        } catch (Exception e) {
            log.error("Failed for input: {}", input, e);
            return "UNKNOWN";
        }
    }

    public Map<String, Integer> getUniqueGeoCount(List<String> ipList) {
        Set<String> uniqueCountries = new HashSet<>();
        Set<String> uniqueCountryCodes = new HashSet<>();
        Set<String> uniqueCities = new HashSet<>();

        if (ipList != null) {
            for (String ip : ipList) {
                try {
                    String country = getCountryByIp(ip);
                    if (!"UNKNOWN".equals(country)) {
                        uniqueCountries.add(country);
                    }

                    String countryCode = getCountryCodeByIp(ip);
                    if (!"UNKNOWN".equals(countryCode)) {
                        uniqueCountryCodes.add(countryCode);
                    }

                    String city = getCityByIp(ip);
                    if (!"UNKNOWN".equals(city)) {
                        uniqueCities.add(city);
                    }
                } catch (Exception e) {
                    log.error("Error processing IP: {}", ip, e);
                }
            }
        }

        return Map.of(
                "uniqueCountries", uniqueCountries.size(),
                "uniqueCountryCodes", uniqueCountryCodes.size(),
                "uniqueCities", uniqueCities.size()
        );
    }

    public String getCountryCode(String input) {
        try {
            CountryResponse response = extractCountry(input);

            return response.getCountry().getIsoCode();
        } catch (Exception e) {
            log.error("Failed for input: {}", input, e);
            return "UNKNOWN";
        }
    }

    public String getCityByIp(String ip) {
        try {
            InetAddress ipAddress = InetAddress.getByName(ip);
            CityResponse response = cityDbReader.city(ipAddress);

            return response.getCity().getName();
        } catch (Exception e) {
            log.error("Failed to get city for IP: {}", ip, e);
            return "UNKNOWN";
        }
    }

    public String getCountryByIp(String ip) {
        try {
            InetAddress ipAddress = InetAddress.getByName(ip);
            CountryResponse response = countryDbReader.country(ipAddress);

            return response.getCountry().getName();
        } catch (Exception e) {
            log.error("Failed to get country for IP: {}", ip, e);
            return "UNKNOWN";
        }
    }

    public String getCountryCodeByIp(String ip) {
        try {
            InetAddress ipAddress = InetAddress.getByName(ip);
            CountryResponse response = countryDbReader.country(ipAddress);

            return response.getCountry().getIsoCode();
        } catch (Exception e) {
            log.error("Failed to get country code for IP: {}", ip, e);
            return "UNKNOWN";
        }
    }

    private CountryResponse extractCountry(String input) throws IOException, GeoIp2Exception {
        String domain = extractDomain(input);

        InetAddress ip = InetAddress.getByName(domain);

        return countryDbReader.country(ip);
    }

    private String extractDomain(String input) {
        if (input == null || input.isEmpty()) {
            throw new IllegalArgumentException("Input is empty");
        }

        String cleaned = input.replaceAll("^https?://", "");
        cleaned = cleaned.split("/")[0];
        cleaned = cleaned.split(":")[0];

        return cleaned;
    }
}