package restriction

import (
	"encoding/json"
	"fmt"

	"github.com/bytedance/sonic"
	"github.com/duke-git/lancet/v2/pointer"
	"github.com/gogf/gf/v2/encoding/gcharset"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/gclient"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"golang.org/x/net/context"
)

type PcOnlineClient struct {
}

func (receiver *PcOnlineClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().GetBytes(ctx, fmt.Sprintf("https://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true", ip))
	ret := PCOnlineResponse{}
	//修改返回数据的编码
	responseU8, _ := gcharset.ToUTF8("GBK", gconv.String(response))
	err = sonic.UnmarshalString(responseU8, &ret)
	if err != nil {
		return
	}
	isp := ""
	addList := gstr.Split(ret.RegionNames, "")
	if len(addList) > 0 {
		isp = addList[len(addList)-1]
	}
	return Response{
		City:     ret.City,
		Isp:      isp,
		Province: ret.Pro,
		Region:   ret.Region,
		RawData:  responseU8,
	}, nil
}

type CSDNClient struct {
}

func (receiver *CSDNClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().
		SetTimeout(gtime.S*2).
		GetBytes(ctx, fmt.Sprintf("https://searchplugin.csdn.net/api/v1/ip/get?ip=%s&", ip))
	ret := CSDNResponse{}
	err = sonic.Unmarshal(response, &ret)
	if err != nil {
		return
	}
	addList := gstr.Split(ret.Data.Address, " ")
	if len(addList) != 4 {
		return
	}
	return Response{
		City:     addList[2],
		Isp:      addList[3],
		Province: addList[1],
		Region:   "",
		RawData:  gconv.String(response),
	}, nil
}

type OlToolsClient struct {
}

func (receiver *OlToolsClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().
		SetTimeout(gtime.S*2).
		GetBytes(ctx, fmt.Sprintf("https://ipwho.is/%s?lang=zh-CN", ip))
	ret := OlToolsResponse{}
	err = json.Unmarshal(response, &ret)
	if err != nil {
		return
	}
	return Response{
		City:     ret.City,
		Isp:      "",
		Province: gstr.TrimLeft(ret.Region, "中国"),
		Region:   "",
		RawData:  gconv.String(response),
	}, nil
}

type IdCdClient struct {
}

func (receiver *IdCdClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().SetTimeout(gtime.S*2).
		PostBytes(ctx, "https://www.idcd.com/tool/ip-json", g.Map{
			"ip": ip,
		})
	ret := IdCdResponse{}
	err = json.Unmarshal(response, &ret)
	if err != nil {
		return
	}
	return Response{
		City:     ret.Data.City,
		Isp:      ret.Data.Isp,
		Province: ret.Data.Region,
		Region:   "",
		RawData:  gconv.String(response),
	}, nil
}

type MeiTuClient struct {
}

func (receiver *MeiTuClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().SetTimeout(gtime.S*2).GetBytes(ctx, fmt.Sprintf("https://webapi-pc.meitu.com/common/ip_location?ip=%s", ip))
	ret := MeiTuResponse{}
	output, err := sonic.Get(response)
	if err != nil {
		return
	}
	node := output.Get("data").Get(ip)
	if !pointer.IsNil(node) {
		jsonData, err2 := node.MarshalJSON()
		if err2 != nil {
			return resp, err2
		}
		if err = sonic.Unmarshal(jsonData, &ret); err != nil {
			return
		}
		return Response{
			City:     gstr.TrimRight(ret.City, "市"),
			Isp:      ret.Isp,
			Province: ret.Province,
			Region:   "",
			RawData:  gconv.String(response),
		}, nil
	}
	return
}

type VoClient struct {
}

func (receiver *VoClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().SetTimeout(gtime.S*2).GetBytes(ctx, fmt.Sprintf("https://api.vore.top/api/IPdata?ip=%s", ip))
	ret := VoResponse{}
	err = json.Unmarshal(response, &ret)
	if err != nil {
		return
	}
	return Response{
		City:     ret.IpData.Info2,
		Isp:      ret.IpData.Isp,
		Province: ret.IpData.Info1,
		Region:   ret.IpData.Info3,
		RawData:  gconv.String(response),
	}, nil
}

type DBIPClient struct {
}

func (receiver *DBIPClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().SetTimeout(gtime.S*2).GetBytes(ctx, fmt.Sprintf("https://api.db-ip.com/v2/free/%s", ip))
	ret := DBIPResponse{}
	err = json.Unmarshal(response, &ret)
	if err != nil {
		return
	}
	city := ret.City
	if len(gstr.Split(city, " ")) > 1 {
		city = gstr.Split(city, " ")[0]
	}
	return Response{
		City:     city,
		Isp:      "",
		Province: ret.StateProv,
		Region:   "",
		RawData:  gconv.String(response),
	}, nil
}

type QJQQClient struct {
}

func (receiver *QJQQClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().SetTimeout(gtime.S*2).GetBytes(ctx, fmt.Sprintf("https://api.qjqq.cn/api/district?ip=%s", ip))
	ret := QJQQResponse{}
	err = json.Unmarshal(response, &ret)
	if err != nil {
		return
	}
	return Response{
		City:     ret.Data.City,
		Isp:      ret.Data.Isp,
		Province: ret.Data.Prov,
		Region:   "",
		RawData:  gconv.String(response),
	}, nil
}

type IQIYIClient struct {
}

func (receiver *IQIYIClient) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().SetTimeout(gtime.S*2).GetBytes(ctx, fmt.Sprintf("https://mesh.if.iqiyi.com/aid/ip/info?version=1.1.1&ip=%s", ip))
	ret := IQIYIResponse{}
	err = json.Unmarshal(response, &ret)
	if err != nil {
		return
	}
	return Response{
		City:     ret.Data.CityCN,
		Isp:      ret.Data.IspCN,
		Province: ret.Data.ProvinceCN,
		Region:   ret.Data.CountryCN,
		RawData:  gconv.String(response),
	}, nil
}

type IP66Client struct {
}

func (receiver *IP66Client) QueryLocationByIP(ctx context.Context, ip string) (resp Response, err error) {
	response := gclient.New().SetTimeout(gtime.S*2).GetBytes(ctx, fmt.Sprintf("https://app.ipdatacloud.com/v2/query?ip=%s&domain&key=5de783e29c4811efa57b00163e167ffb", ip))
	ret := IP66Response{}
	err = json.Unmarshal(response, &ret)
	if err != nil {
		return
	}
	return Response{
		City:     ret.Data.Location.City,
		Isp:      ret.Data.Location.Isp,
		Province: ret.Data.Location.Province,
		Region:   "",
		RawData:  gconv.String(response),
	}, nil
}
