package common

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"regexp"
	"strings"
	"unicode"
)

var (
	environment = map[string]string{}
	wordRegexp  = regexp.MustCompile(`\w+`)
)

// Setenv - cria ou atualiza uma variavel de ambiente dentro da aplicação
func Setenv(id, value string) {
	environment[id] = value
}

// Getenv - recupera uma variavel de ambiente dentro da aplicação
func Getenv(id string, fallback ...string) (value string) {
	var exist bool

	if value, exist = environment[id]; !exist {
		for _, value = range fallback {
			break
		}
	}
	return value
}

func LcFirst(str string) string {
	for i, v := range str {
		return string(unicode.ToLower(v)) + str[i+1:]
	}
	return ""
}
func UpFirst(str string) string {
	for i, v := range str {
		return string(unicode.ToUpper(v)) + str[i+1:]
	}
	return ""
}
func JSONStringfy(v interface{}) (string, error) {
	o, err := json.Marshal(v)
	if err != nil {
		return "", err
	}
	return string(o), nil
}

// FileExists reports whether the named file or directory exists.
func FileExists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

func ResolveParams(format string, data map[string]interface{}) string {
	for k, v := range data {
		format = strings.Replace(
			format,
			fmt.Sprintf("${%s}", k),
			fmt.Sprintf("%v", v),
			-1,
		)
	}
	return format
}

func UnderToCamel(input string) string {
	input = strings.ToLower(input)
	input = strings.ReplaceAll(input, "_", " ")
	input = strings.Title(input)
	return strings.ReplaceAll(input, " ", "")
}

func Mkdir(perm os.FileMode, paths ...string) (err error) {

	for _, path := range paths {
		if _, err := os.Stat(path); os.IsNotExist(err) {

			if err = os.MkdirAll(path, perm); err != nil {
				return err
			}
		}
	}
	return
}

func JsonParseMode(path, mode string, v interface{}) (err error) {
	defer func() {
		if rec := recover(); rec != nil {
			err = fmt.Errorf("%s", rec)
		}
	}()
	if err = ParseJson(path+".json", v); err != nil {
		return
	}
	// fmt.Println(fmt.Sprintf("%s.%s.json", path, mode))

	err = ParseJson(fmt.Sprintf("%s.%s.json", path, mode), v, true)
	return
}

func ParseJson(filename string, v interface{}, ignoreFileExists ...bool) error {
	// fmt.Println("load file ", filename)
	data, err := FileGetContents(filename)

	if err != nil {
		if len(ignoreFileExists) > 0 && ignoreFileExists[0] {
			return nil
		}
		return err
	}

	if err = json.Unmarshal([]byte(data), v); err != nil {
		return err
	}

	return nil
}

func CamelToUnder(input string) string {
	out := camelToUnderRegex.ReplaceAllStringFunc(input, func(m string) string {
		return "_" + strings.ToLower(m)
	})
	return strings.TrimLeft(out, "_")
}
func FilePutContents(filename string, content string, perm os.FileMode) error {
	return FilePutContentsBytes(filename, []byte(content), perm)
}

func FilePutContentsBytes(filename string, content []byte, perm os.FileMode) error {

	var (
		dir = path.Dir(filename)
	)

	if _, err := os.Stat(dir); os.IsNotExist(err) {

		if err = os.MkdirAll(dir, perm); err != nil {
			return err
		}
	}
	return ioutil.WriteFile(filename, []byte(content), 0644)
}

func RemoveFile(file string) error {

	if err := os.Remove(file); err != nil {
		return err
	}
	return nil
}

func GetFiles(directory string) ([]os.FileInfo, error) {

	files, err := ioutil.ReadDir(directory)
	if err != nil {
		return []os.FileInfo{}, err
	}
	return files, nil
}
func FileGetContents(filename string) (string, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func WriteToJson(path string, object interface{}) (err error) {
	var data []byte

	if data, err = json.MarshalIndent(object, "", "    "); err == nil {
		err = FilePutContentsBytes(path, data, 0777)
	}

	return
}

func IsEmpty(reference interface{}) bool {
	return isEmptyReflection(reflect.ValueOf(reference))
}

func isEmptyReflection(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.String, reflect.Array:
		return v.Len() == 0
	case reflect.Map, reflect.Slice:
		return v.Len() == 0 || v.IsNil()
	// case reflect.Bool:
	// 	return !v.Bool()
	// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
	// 	return v.Int() == 0
	// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
	// 	return v.Uint() == 0
	// case reflect.Float32, reflect.Float64:
	// 	return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
