package utils

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
)

func IsPublicIP(ipString string) bool {
	ip := net.ParseIP(ipString)
	if ip.IsLoopback() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() {
		return false
	}
	if ip4 := ip.To4(); ip4 != nil {
		switch true {
		case ip4[0] == 10:
			return false
		case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
			return false
		case ip4[0] == 192 && ip4[1] == 168:
			return false
		default:
			return true
		}
	}
	return false
}

// Get Location IP .
func GetLocationIP() (localIp string) {
	localIp = "127.0.0.1"
	// Get all network interfaces
	interfaces, err := net.Interfaces()
	if err != nil {
		return
	}

	for _, iface := range interfaces {
		// Skip the loopback interface
		if iface.Flags&net.FlagLoopback != 0 {
			continue
		}

		// Get addresses associated with the interface
		addrs, err := iface.Addrs()
		if err != nil {
			continue
		}

		for _, addr := range addrs {
			// Check if the address is an IPNet
			ipnet, ok := addr.(*net.IPNet)
			if !ok || ipnet.IP.IsLoopback() {
				continue
			}

			// Get the IP address
			ip := ipnet.IP.To4()
			if ip == nil {
				continue
			}

			// Skip IP addresses in the 169.254.x.x range
			if strings.HasPrefix(ip.String(), "169.254") {
				continue
			}

			// Skip IP addresses in the 169.254.x.x range
			if strings.HasPrefix(ip.String(), "26.26") {
				continue
			}

			// Return the first valid IP address found
			return ip.String()
		}
	}

	return
}

func LocalIPv4s() ([]string, error) {

	var ips []string
	addrs, _ := net.InterfaceAddrs()

	for _, addr := range addrs {
		if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				locIP := ipnet.IP.To4().String()
				if locIP[0:7] != "169.254" {
					ips = append(ips, locIP)
				}
			}
		}
	}

	return ips, nil
}

func GetOutBoundIP() (ip string, err error) {
	body, err := HttpGet("http://ip.dhcp.cn/?ip") // 获取外网 IP
	return string(body), err
}

func HttpGet(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		// handle error
		return nil, err
	}

	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)

	return body, err
}

func HttpPost(url string, header map[string]string, data []byte) ([]byte, error) {
	var err error
	reader := bytes.NewBuffer(data)
	request, err := http.NewRequest("POST", url, reader)

	if err != nil {
		return nil, err
	}

	request.Header.Set("Content-Type", "application/json;charset=UTF-8")
	request.Header.Set("Request-Id", ULID())

	for key, val := range header {
		request.Header.Set(key, val)
	}

	client := http.Client{}
	resp, err := client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, errors.New(string(respBytes))
	}

	return respBytes, nil
}

func HttpRequest(r *http.Request) ([]byte, error) {
	var err error
	client := http.Client{}
	resp, err := client.Do(r)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return respBytes, nil
}

func DownloadFile(url, saveTo string, fb func(length, downLen int64)) {
	var (
		fsize   int64
		buf     = make([]byte, 32*1024)
		written int64
	)
	//创建一个http client
	client := new(http.Client)
	//get方法获取资源
	resp, err := client.Get(url)
	if err != nil {
		fmt.Printf("download %s error:%s\n", url, err)
		os.Exit(0)
	}
	//读取服务器返回的文件大小
	fsize, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 32)
	if err != nil {
		fmt.Println(err)
	}
	//创建文件
	file, err := os.Create(saveTo)
	file.Chmod(0777)
	if err != nil {
		fmt.Printf("Create %s error:%s\n", saveTo, err)
		os.Exit(0)
	}
	defer file.Close()
	if resp.Body == nil {
		fmt.Printf("resp %s error:%s\n", url, err)
		os.Exit(0)
	}
	defer resp.Body.Close()
	//下面是 io.copyBuffer() 的简化版本
	for {
		//读取bytes
		nr, er := resp.Body.Read(buf)
		if nr > 0 {
			//写入bytes
			nw, ew := file.Write(buf[0:nr])
			//数据长度大于0
			if nw > 0 {
				written += int64(nw)
			}
			//写入出错
			if ew != nil {
				err = ew
				break
			}
			//读取是数据长度不等于写入的数据长度
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if er != nil {
			if er != io.EOF {
				err = er
			}
			break
		}
		//没有错误了快使用 callback

		fb(fsize, written)
	}

	if err != nil {
		fmt.Printf("callback error:%s\n", err)
		os.Exit(0)
	}
}