package com.apobates.forum.event.utils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import java.util.Optional;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbMakerConfigException;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * IP地址匹配器,使用开源项目(ip2region) {@link https://gitee.com/lionsoul/ip2region}
 * 
 * @author xiaofanku
 * @since 20200527
 */
public final class IpMatcher {
    /**
     * 失败时的返回的占位符
     */
    public final static String MARK = "-";
    private static IpMatcher instance = null;
    private final DbSearcher searcher;
    private final static Logger logger = LoggerFactory.getLogger(IpMatcher.class);
    
    static {
        try {
            instance = new IpMatcher();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[IPM]instace exception: " + e.getMessage());
            }
        }
    }
    
    public static IpMatcher getInstance() {
        return instance;
    }
    
    private IpMatcher() throws DbMakerConfigException, IOException {
        DbConfig config = new DbConfig();
        this.searcher = new DbSearcher(config, loadDBBytes());
    }
    
    private byte[] loadDBBytes() throws IOException {
        // db
        InputStream istream = this.getClass().getClassLoader().getResourceAsStream("META-INF/ip2region.db");
        return istream.readAllBytes();
    }
    
    /**
     * 查找方法
     *
     * @param ip ip地址
     * @return
     * @throws IllegalStateException 对查找过程产生异常的包装
     * @throws IllegalArgumentException ip地址检查失败时抛出
     * @throws NullPointerException 若参数是null时抛出
     */
    private String getCityInfo(String ip) throws IllegalStateException, IllegalArgumentException, NullPointerException {
        Objects.requireNonNull(ip);
        try {
            if (Util.isIpAddress(ip) == false) {
                throw new IllegalArgumentException("参数不符合合法的地址");
            }
            DataBlock dataBlock = searcher.memorySearch(ip);//.btreeSearch(ip);
            return dataBlock.getRegion();
        } catch (NullPointerException | IOException e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[IPM]get city info exception: " + e.getMessage());
            }
            throw new IllegalStateException(e);
        }
    }
    
    /**
     * 返回字符串的匹配结果
     *
     * @param ipAddr ip地址
     * @return
     */
    public Optional<String> matchToString(String ipAddr) {
        if (isLoopbackIp(ipAddr)) {
            return Optional.empty();
        }
        try {
            String data = getCityInfo(ipAddr);
            return Optional.of(data);
        } catch (IllegalStateException | IllegalArgumentException | NullPointerException e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[IPM]matchToString exception: " + e.getMessage());
            }
        }
        return Optional.empty();
    }
    
    /**
     * 返回IpMatchResult的匹配结果
     *
     * @param ipAddr ip地址
     * @return
     */
    public Optional<IpMatchResult> matchToResult(String ipAddr) {
        if (isLoopbackIp(ipAddr)) {
            return Optional.empty();
        }
        try {
            String[] data = getCityInfo(ipAddr).split("\\|"); //国家|大区|省份|城市|运营商
            return Optional.of(new IpMatchResult(ipAddr, data[4], data[2], data[3], MARK));
        } catch (IllegalStateException | IllegalArgumentException | NullPointerException | ArrayIndexOutOfBoundsException e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[IPM]matchToResult exception: " + e.getMessage());
            }
        }
        return Optional.empty();
    }
    
    /**
     * 是否是本地的环回地址
     *
     * @param ipAddr ip地址
     * @return true是,false不是
     */
    public static boolean isLoopbackIp(String ipAddr) {
        try {
            return Inet4Address.getByName(ipAddr).isLoopbackAddress();
        } catch (UnknownHostException e) {
            return false;
        }
    }
}