package persistence

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
)

func toSqlArgs(rec interface{}) (map[string]interface{}, error) {
	// Convert to JSON...
	b, err := json.Marshal(rec)
	if err != nil {
		return nil, err
	}

	// ... then convert to map
	var m map[string]interface{}
	err = json.Unmarshal(b, &m)
	r := make(map[string]interface{}, len(m))
	for f, v := range m {
		r[toSnakeCase(f)] = v
	}
	return r, err
}

var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")

func toSnakeCase(str string) string {
	snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
	snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
	return strings.ToLower(snake)
}

func ToStruct(m map[string]interface{}, rec interface{}, fieldNames []string) error {
	var r = make(map[string]interface{}, len(m))
	for _, f := range fieldNames {
		v, ok := m[f]
		if !ok {
			return fmt.Errorf("invalid field '%s'", f)
		}
		r[toCamelCase(f)] = v
	}
	// Convert to JSON...
	b, err := json.Marshal(r)
	if err != nil {
		return err
	}

	// ... then convert to struct
	err = json.Unmarshal(b, &rec)
	return err
}

var matchUnderscore = regexp.MustCompile("_([A-Za-z])")

func toCamelCase(str string) string {
	return matchUnderscore.ReplaceAllStringFunc(str, func(s string) string {
		return strings.ToUpper(strings.Replace(s, "_", "", -1))
	})
}