package goscrobble

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"math/big"
	"net"
	"net/http"
	"regexp"
	"strings"
	"time"

	"github.com/google/uuid"
)

// DevMode - Controls background workers and probably more
var DevMode bool

var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
var usernameRegex = regexp.MustCompile("^[a-zA-Z0-9_\\.]+$")

// decodeJson - Returns a map[string]interface{}
func decodeJson(body io.ReadCloser) (map[string]interface{}, error) {
	var jsonInput map[string]interface{}
	decoder := json.NewDecoder(body)
	err := decoder.Decode(&jsonInput)

	return jsonInput, err
}

// isEmailValid - checks if the email provided passes the required structure and length.
func isEmailValid(e string) bool {
	if len(e) < 5 && len(e) > 254 {
		return false
	}

	if !emailRegex.MatchString(e) {
		return false
	}

	// Do MX lookup
	parts := strings.Split(e, "@")
	mx, err := net.LookupMX(parts[1])
	if err != nil || len(mx) == 0 {
		return false
	}

	return true
}

// isUsernameValid - Checks if username is alphanumeric+underscores+dots
func isUsernameValid(e string) bool {
	if len(e) > 64 {
		return false
	}

	return usernameRegex.MatchString(e)
}

// contains - Check if string is in list
func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// getUserIp - Returns IP that isn't set in REVERSE_PROXIES
func getUserIp(r *http.Request) net.IP {
	var ip net.IP
	host, _, _ := net.SplitHostPort(r.RemoteAddr)
	if contains(ReverseProxies, host) {
		forwardedFor := r.Header.Get("X-Forwarded-For")
		if !contains(ReverseProxies, forwardedFor) {
			host = forwardedFor
		} else {
			realIp := r.Header.Get("X-Real-IP")
			if !contains(ReverseProxies, realIp) {
				host = realIp
			}
		}
	}

	if host == "" {
		host = "0.0.0.0"
	}

	ip = net.ParseIP(host)
	return ip
}

// Inet_Aton converts an IPv4 net.IP object to a 64 bit integer.
func Inet_Aton(ip net.IP) int64 {
	ipv4Int := big.NewInt(0)
	ipv4Int.SetBytes(ip.To4())
	return ipv4Int.Int64()
}

// Inet6_Aton converts an IP Address (IPv4 or IPv6) net.IP object to a hexadecimal
// representaiton. This function is the equivalent of
// inet6_aton({{ ip address }}) in MySQL.
func Inet6_Aton(ip net.IP) string {
	ipv4 := false
	if ip.To4() != nil {
		ipv4 = true
	}

	ipInt := big.NewInt(0)
	if ipv4 {
		ipInt.SetBytes(ip.To4())
		ipHex := hex.EncodeToString(ipInt.Bytes())
		return ipHex
	}

	ipInt.SetBytes(ip.To16())
	ipHex := hex.EncodeToString(ipInt.Bytes())
	return ipHex
}

// calcPageOffsetString - Used to SQL paging
func calcPageOffsetString(page int, offset int) string {
	return fmt.Sprintf("%d", page*offset)
}

// timestampToSeconds - Converts HH:MM:SS to (int)seconds
func timestampToSeconds(timestamp string) int {
	var h, m, s int
	n, err := fmt.Sscanf(timestamp, "%d:%d:%d", &h, &m, &s)
	if err != nil || n != 3 {
		return 0
	}
	return h*3600 + m*60 + s
}

func filterSlice(s []string) []string {
	m := make(map[string]bool)
	for _, item := range s {
		if item != "" {
			if _, ok := m[strings.TrimSpace(item)]; !ok {
				m[strings.TrimSpace(item)] = true
			}
		}
	}

	var result []string
	for item := range m {
		result = append(result, item)
	}

	return result
}

func isValidTimezone(tz string) bool {
	_, err := time.LoadLocation(tz)
	return err == nil
}

func getMd5(val string) string {
	hash := md5.Sum([]byte(val))
	return hex.EncodeToString(hash[:])
}

func newUUID() string {
	return uuid.New().String()
}
