const RESERVED_IP = 'Reserved IP';
const IP_REGION_CACHE = 'ip-region-cache';

async function getIpRegionFromIpApi(ip: string): Promise<string> {
  const url = `https://ipapi.co/${ip}/json/`;
  const result = await fetch(url);
  const data = await result.json();

  let region = '';
  if (data.error) {
    if (data.reserved) {
      region = RESERVED_IP;
    }
  } else {
    region = [data.country_name, data.region, data.city].filter(Boolean).join('/');
  }
  return region;
}
async function getIpRegionFromIpSb(ip: string): Promise<string> {
  const url = `https://api.ip.sb/geoip/${ip}`;
  const result = await fetch(url);
  const data = await result.json();

  let region = '';
  if (data.error) {
    if (data.reserved) {
      region = RESERVED_IP;
    }
  } else {
    region = [data.country, data.region, data.city].filter(Boolean).join('/');
  }
  return region;
}

export async function ip2Region(ip: string): Promise<string> {
  let region = '';
  let ipRegionCache: Record<string, string> = {};
  try {
    const tmpCache = localStorage.getItem(IP_REGION_CACHE);
    if (tmpCache) {
      ipRegionCache = JSON.parse(tmpCache);
    }
  } catch {
    ipRegionCache = {};
  }
  if (ipRegionCache[ip]) {
    region = ipRegionCache[ip];
  } else {
    try {
      region = await getIpRegionFromIpSb(ip);
    } catch {
      console.warn('Fail to get ip region from ip.sb');
    }

    if (!region) {
      try {
        region = await getIpRegionFromIpApi(ip);
      } catch {
        console.warn('Fail to get ip region from ip-api.co');
      }
    }
  }

  if (region) {
    // NOTICE: 为了防止 cache 过大，将在 size 超过 100 时，删除最旧的数据
    const ipList = Object.keys(ipRegionCache);
    if (ipList.length > 100) {
      delete ipRegionCache[ipList[0]];
    }
    if (ipRegionCache[ip]) {
      delete ipRegionCache[ip];
    }
    ipRegionCache[ip] = region;
    localStorage.setItem(IP_REGION_CACHE, JSON.stringify(ipRegionCache));
  }
  return region;
}

// eslint-disable-next-line require-await
export async function batchIp2Region(ips: string[]): Promise<string[]> {
  return Promise.all(ips.map((ip) => ip2Region(ip)));
}
