package quant.rich.emoney.util;

import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.model.CityResponse;
import com.maxmind.geoip2.record.Subdivision;

import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import quant.rich.emoney.client.OkHttpClientProvider;
import quant.rich.emoney.entity.sqlite.ProxySetting;
import quant.rich.emoney.pojo.dto.IpInfo;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;
import org.springframework.core.io.ClassPathResource;

@Slf4j
public class GeoIPUtil {

    private static DatabaseReader cityReader;
    public static String LOCALE = "zh-CN";

    static {
        try {
            ClassPathResource geoLite2CityResource = new ClassPathResource("/conf/extra/GeoLite2-City.mmdb");
            cityReader = new DatabaseReader.Builder(geoLite2CityResource.getInputStream()).build();
        } catch (IOException e) {
            throw new RuntimeException("IP 地址库初始化失败", e);
        }
    }

    public static IpInfo getIpInfoThroughProxy(ProxySetting proxySetting) {
        if (proxySetting == null) {
            throw new RuntimeException("代理为空");
        }
    	return CallerLockUtil.tryCallWithCallerLock(() -> {
            Proxy proxy = proxySetting.getProxy();
            boolean ignoreHttpsVerification = proxySetting.getIgnoreHttpsVerification();
            // OkHttp 客户端配置
            OkHttpClient client = OkHttpClientProvider.getInstance(
                    proxy, ignoreHttpsVerification,
                    builder -> builder
                        .connectTimeout(10, TimeUnit.SECONDS)
                        .readTimeout(10, TimeUnit.SECONDS));
    
            // 使用 httpbin.org/ip 获取当前请求的公网 IP
            Request requestIpv4 = new Request.Builder()
                    .url("https://ipv4.icanhazip.com")
                    .build();
            
            IpInfo ipInfo = new IpInfo();
            
            try (Response response = client.newCall(requestIpv4).execute()) {
                if (!response.isSuccessful()) {
                    log.warn("Request ipv4 failed with code: {}", response.code());
                    return IpInfo.EMPTY;
                }
    
                String responseBody = response.body().string();
                log.debug("Response ipv4 from proxy: {}", responseBody.trim());
                ipInfo.setIp(responseBody);
            } catch (Exception e) {
                log.warn("Proxy ipv4 error: {}", e.getMessage());
                return IpInfo.EMPTY;
            }
            Request requestIpv6 = new Request.Builder()
                    .url("https://ipv6.icanhazip.com")
                    .build();
            try (Response response = client.newCall(requestIpv6).execute()) {
                if (!response.isSuccessful()) {
                    log.warn("Request ipv6 failed with code: {}", response.code());
                }
    
                String responseBody = response.body().string();
                log.debug("Response ipv6 from proxy: {}", responseBody.trim());
                ipInfo.setIpv6(responseBody);
            } catch (Exception e) {
                log.warn("Proxy ipv6 error {}", e.getMessage());
            }
            return queryIpInfoGeoLite(ipInfo);
    	}, 100, proxySetting).orElse(IpInfo.EMPTY);
    }
    
    /**
     * 以 IPv4 为准
     * @param ipInfo
     */
    public static IpInfo queryIpInfoGeoLite(IpInfo ipInfo) {
        try {
            InetAddress ipv4Address = InetAddress.getByName(ipInfo.getIp());
            CityResponse response = cityReader.city(ipv4Address);
            
            if (response.getCountry() != null) {
                String countryStr = response.getCountry().getNames().get(LOCALE);
                ipInfo.setCountry(countryStr);
            }
            
            if (response.getSubdivisions() != null && !response.getSubdivisions().isEmpty()) {
                // 省
                Subdivision subdivision = response.getSubdivisions().getFirst();
                if (subdivision != null) {
                    String subdivisionStr = subdivision.getNames().get(LOCALE);
                    ipInfo.setSubdivision(subdivisionStr);
                }
            }
            if (response.getCity() != null) {
                // 市
                String cityStr = response.getCity().getNames().get(LOCALE);
                ipInfo.setCity(cityStr);
            }
        } catch (IOException | GeoIp2Exception e) {
            log.warn("Get city response from GeoLite2-City database failed: {}", e.getMessage());
        }
        log.debug("ipInfo: {}", ipInfo);
        return ipInfo;
    }
    
    public static void main(String[] args) {
        System.out.format("GeoLite2-City %s\r\n", queryIpInfoGeoLite(new IpInfo().setIp("46.3.98.216")));
    }
}
