package com.apobates.forum.utils.ip;

import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.Objects;
import java.util.Optional;

import org.apache.commons.io.IOUtils;
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 20191206
 */
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 IOUtils.toByteArray(istream);
	}
	/**
	 * 查找方法
	 * 
	 * @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.ofNullable(data);
		}catch(IllegalStateException | IllegalArgumentException | NullPointerException e){
			if(logger.isDebugEnabled()){
				logger.debug("[IPM]matchToString exception: "+e.getMessage());
			}
			return Optional.empty(); //failure(e.getMessage());
		}
	}
	
	/**
	 * 返回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(); //failure(e.getMessage());
		}
	}
	
	/**
	 * 是否是本地的环回地址
	 * 
	 * @param ipAddr ip地址
	 * @return true是,false不是
	 */
	public static boolean isLoopbackIp(String ipAddr){
		try{
			return Inet4Address.getByName(ipAddr).isLoopbackAddress();
		}catch(UnknownHostException e){
			return false;
		}
	}
	
	public static class IpMatchResult{
		private final String ipAddr;
		//供应商:联通|电信
		private final String isp;
		//地域:省
		private final String province;
		//地域:市
		private final String city;
		//地域:区
		private final String district;
		
		public IpMatchResult(String ipAddr, String isp, String province, String city, String district) {
			this.ipAddr = ipAddr;
			this.isp = isp;
			this.province = province;
			this.city = city;
			this.district = district;
		}
		/**
		 * 返回ISP供应商,例:联通
		 * @return
		 */
		public String getIsp() {
			return isp;
		}
		/**
		 * 返回省份,例:山东省
		 * @return
		 */
		public String getProvince() {
			return province;
		}
		/**
		 * 返回市,例:烟台市
		 * @return
		 */
		public String getCity() {
			return city;
		}
		/**
		 * 返回区或县
		 * @return
		 */
		public String getDistrict() {
			return district;
		}
		
		public String getIpAddr() {
			return ipAddr;
		}
		
		@Override
		public String toString() {
			return String.format("{\"IP\":\"%s\", \"ISP供应商\":\"%s\", \"省\":\"%s\", \"市\": \"%s\"}", getIpAddr(), getIsp(), getProvince(), getCity());
		}
	}
}
