package formbinder

import (
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"time"
)

const tagName = "form"

var timeFormat = []string{
	"2006-01-02",
	time.ANSIC,
	time.UnixDate,
	time.RubyDate,
	time.RFC822,
	time.RFC822Z,
	time.RFC850,
	time.RFC1123,
	time.RFC1123Z,
	time.RFC3339,
	time.RFC3339Nano,
	time.Kitchen,
	time.Stamp,
	time.StampMilli,
	time.StampMicro,
	time.StampNano,
}

// A pathMap holds the values of a map with its key and values correspondent
type pathMap struct {
	ma    reflect.Value
	key   string
	value reflect.Value

	path string
}

// a pathMaps holds the values for each key
type pathMaps []*pathMap

// find find and get the value by the given key
func (ma pathMaps) find(id reflect.Value, key string) *pathMap {
	for _, v := range ma {
		if v.ma == id && v.key == key {
			return v
		}
	}
	return nil
}

// DecodeCustomTypeFunc is a function that indicate how should to decode a custom type
type DecodeCustomTypeFunc func([]string) (interface{}, error)

// DecodeCustomTypeField is a function registered for a specific field of the struct passed to the Decoder
type DecodeCustomTypeField struct {
	field reflect.Value
	fun   DecodeCustomTypeFunc
}

// DecodeCustomType fields for custom types
type DecodeCustomType struct {
	fun    DecodeCustomTypeFunc
	fields []DecodeCustomTypeField
}

// Decoder the main to decode the values
type Decoder struct {
	main       reflect.Value
	formValues url.Values
	opts       *DecoderOptions

	curr   reflect.Value
	value  string
	values []string

	path    string
	field   string
	bracket string
	isKey   bool

	maps pathMaps

	customTypes map[reflect.Type]*DecodeCustomType
}

// DecoderOptions options for decoding the values
type DecoderOptions struct {
	TagName string
}

// RegisterCustomType It is the method responsible for register functions for decoding custom types
func (dec *Decoder) RegisterCustomType(fn DecodeCustomTypeFunc, types []interface{}, fields []interface{}) *Decoder {
	if dec.customTypes == nil {
		dec.customTypes = make(map[reflect.Type]*DecodeCustomType)
	}
	for i := range types {
		typ := reflect.TypeOf(types[i])
		if dec.customTypes[typ] == nil {
			dec.customTypes[typ] = new(DecodeCustomType)
		}
		if len(fields) > 0 {
			for j := range fields {
				va := reflect.ValueOf(fields[j])
				f := DecodeCustomTypeField{field: va, fun: fn}
				dec.customTypes[typ].fields = append(dec.customTypes[typ].fields, f)
			}
		} else {
			dec.customTypes[typ].fun = fn
		}
	}
	return dec
}

// NewDecoder creates a new instance of Decoder
func NewDecoder(opts *DecoderOptions) *Decoder {
	dec := &Decoder{opts: opts}
	if dec.opts == nil {
		dec.opts = &DecoderOptions{}
	}
	if dec.opts.TagName == "" {
		dec.opts.TagName = tagName
	}
	return dec
}

// Decode decodes the url.Values into a element that must be a pointer to a type provided by argument
func (dec *Decoder) Decode(vs url.Values, dst interface{}) error {
	main := reflect.ValueOf(dst)
	if main.Kind() != reflect.Ptr {
		return newError(fmt.Errorf("formam: the value passed for decode is not a pointer but a %v", main.Kind()))
	}
	dec.main = main.Elem()
	dec.formValues = vs
	return dec.prepare()
}

// Decode decodes the url.Values into a element that must be a pointer to a type provided by argument
func Decode(vs url.Values, dst interface{}) error {
	main := reflect.ValueOf(dst)
	if main.Kind() != reflect.Ptr {
		return newError(fmt.Errorf("formam: the value passed for decode is not a pointer but a %v", main.Kind()))
	}
	dec := &Decoder{
		main:       main.Elem(),
		formValues: vs,
		opts: &DecoderOptions{
			TagName: tagName,
		},
	}
	return dec.prepare()
}

func (dec *Decoder) prepare() error {
	// iterate over the form's values and decode it
	for k, v := range dec.formValues {
		dec.path = k
		dec.field = k
		dec.values = v
		dec.value = v[0]
		dec.curr = dec.main
		if dec.value != "" {
			if err := dec.begin(); err != nil {
				return err
			}
		}
	}
	// set values of maps
	for _, v := range dec.maps {
		key := v.ma.Type().Key()
		// check if the key implements the UnmarshalText interface
		var val reflect.Value
		if key.Kind() == reflect.Ptr {
			val = reflect.New(key.Elem())
		} else {
			val = reflect.New(key).Elem()
		}
		// decode key
		dec.path = v.path
		dec.field = v.path
		dec.values = []string{v.key}
		dec.curr = val
		dec.value = v.key
		dec.isKey = true
		if err := dec.decode(); err != nil {
			return err
		}
		// set key with its value
		v.ma.SetMapIndex(dec.curr, v.value)
	}
	dec.maps = make(pathMaps, 0)
	return nil
}

// begin analyzes the current path to walk through it
func (dec *Decoder) begin() (err error) {
	inBracket := false
	valBracket := ""
	bracketClosed := false
	lastPos := 0
	tmp := dec.field

	// parse path
	for i, char := range tmp {
		if char == '[' && inBracket == false {
			// found an opening bracket
			bracketClosed = false
			inBracket = true
			dec.field = tmp[lastPos:i]
			lastPos = i + 1
			/*
				if err = dec.walk(); err != nil {
					return
				}
			*/
			continue
		} else if inBracket {
			// it is inside of bracket, so get its value
			if char == ']' {
				/*
					nextChar := tmp[i+1:]
					if nextChar != "" {
						t := nextChar[:1]
						if t != "[" && t != "." {
							valBracket += string(char)
							continue
						}
					}
				*/
				// found an closing bracket, so it will be recently close, so put as true the bracketClosed
				// and put as false inBracket and pass the value of bracket to dec.key
				inBracket = false
				bracketClosed = true
				dec.bracket = valBracket
				lastPos = i + 1
				if err = dec.walk(); err != nil {
					return
				}
				valBracket = ""
			} else {
				// still inside the bracket, so follow getting its value
				valBracket += string(char)
			}
			continue
		} else if !inBracket {
			// not found any bracket, so try found a field
			if char == '.' {
				// found a field, we need to know if the field is next of a closing bracket,
				// for example: [0].Field
				if bracketClosed {
					bracketClosed = false
					lastPos = i + 1
					continue
				}
				// found a field, but is not next of a closing bracket, for example: Field1.Field2
				dec.field = tmp[lastPos:i]
				//dec.field = tmp[:i]
				lastPos = i + 1
				if err = dec.walk(); err != nil {
					return
				}
			}
			continue
		}
	}
	// last field of path
	dec.field = tmp[lastPos:]

	return dec.end()
}

// walk traverses the current path until to the last field
func (dec *Decoder) walk() error {
	// check if there is field, if is so, then it should be struct or map (access by .)
	if dec.field != "" {
		// check if is a struct or map
		switch dec.curr.Kind() {
		case reflect.Struct:
			if err := dec.findStructField(); err != nil {
				return err
			}
		case reflect.Map:
			dec.walkInMap(dec.field)
		}
	}
	// check if is a interface and it is not nil. This mean that the interface
	// has a struct, map or slice as value
	if dec.curr.Kind() == reflect.Interface && !dec.curr.IsNil() {
		dec.curr = dec.curr.Elem()
	}
	// check if it is a pointer
	if dec.curr.Kind() == reflect.Ptr {
		if dec.curr.IsNil() {
			dec.curr.Set(reflect.New(dec.curr.Type().Elem()))
		}
		dec.curr = dec.curr.Elem()
	}
	// check if there is access to slice/array or map (access by [])
	if dec.bracket != "" {
		switch dec.curr.Kind() {
		case reflect.Array:
			index, err := strconv.Atoi(dec.bracket)
			if err != nil {
				return newError(fmt.Errorf("formam: the index of array is not a number in the field \"%v\" of path \"%v\"", dec.field, dec.path))
			}
			dec.curr = dec.curr.Index(index)
		case reflect.Slice:
			index, err := strconv.Atoi(dec.bracket)
			if err != nil {
				return newError(fmt.Errorf("formam: the index of slice is not a number in the field \"%v\" of path \"%v\"", dec.field, dec.path))
			}
			if dec.curr.Len() <= index {
				dec.expandSlice(index + 1)
			}
			dec.curr = dec.curr.Index(index)
		case reflect.Map:
			dec.walkInMap(dec.bracket)
		default:
			return newError(fmt.Errorf("formam: the field \"%v\" in path \"%v\" has a index for array but it is a %v", dec.field, dec.path, dec.curr.Kind()))
		}
	}
	dec.field = ""
	dec.bracket = ""
	return nil
}

// walkMap puts in d.curr the map concrete for decode the current value
func (dec *Decoder) walkInMap(key string) {
	n := dec.curr.Type()
	takeAndAppend := func() {
		m := reflect.New(n.Elem()).Elem()
		dec.maps = append(dec.maps, &pathMap{dec.curr, key, m, dec.path})
		dec.curr = m
	}
	if dec.curr.IsNil() {
		dec.curr.Set(reflect.MakeMap(n))
		takeAndAppend()
	} else if a := dec.maps.find(dec.curr, key); a == nil {
		takeAndAppend()
	} else {
		dec.curr = a.value
	}
}

// end finds the last field for decode its value correspondent
func (dec *Decoder) end() error {
	switch dec.curr.Kind() {
	case reflect.Struct:
		if err := dec.findStructField(); err != nil {
			return err
		}
	case reflect.Map:
		// leave backward compatibility for access to maps by .
		dec.walkInMap(dec.field)
	}
	if dec.value == "" {
		return nil
	}
	return dec.decode()
}

// decode sets the value in the field
func (dec *Decoder) decode() error {
	// has registered a custom type? If so, then decode by it
	if ok, err := dec.checkCustomType(); ok || err != nil {
		return err
	}
	// implements UnmarshalText interface? If so, then decode by it
	if ok, err := checkUnmarshalText(dec.curr, dec.value); ok || err != nil {
		return err
	}

	switch dec.curr.Kind() {
	case reflect.Array:
		if dec.bracket == "" {
			// not has index, so to decode all values in the array
			tmp := dec.curr
			for i, v := range dec.values {
				dec.curr = tmp.Index(i)
				dec.value = v
				if err := dec.decode(); err != nil {
					return err
				}
			}
		} else {
			// has index, so to decode value by index indicated
			index, err := strconv.Atoi(dec.bracket)
			if err != nil {
				return newError(fmt.Errorf("formam: the index of array is not a number in the field \"%v\" of path \"%v\"", dec.field, dec.path))
			}
			dec.curr = dec.curr.Index(index)
			return dec.decode()
		}
	case reflect.Slice:
		if dec.bracket == "" {
			// not has index, so to decode all values in the slice/array
			dec.expandSlice(len(dec.values))
			tmp := dec.curr
			for i, v := range dec.values {
				dec.curr = tmp.Index(i)
				dec.value = v
				if err := dec.decode(); err != nil {
					return err
				}
			}
		} else {
			// has index, so to decode value by index indicated
			index, err := strconv.Atoi(dec.bracket)
			if err != nil {
				return newError(fmt.Errorf("formam: the index of slice is not a number in the field \"%v\" of path \"%v\"", dec.field, dec.path))
			}
			if dec.curr.Len() <= index {
				dec.expandSlice(index + 1)
			}
			dec.curr = dec.curr.Index(index)
			return dec.decode()
		}
	case reflect.String:
		dec.curr.SetString(dec.value)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if num, err := strconv.ParseInt(dec.value, 10, 64); err != nil {
			return newError(fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid signed integer number", dec.field, dec.path))
		} else {
			dec.curr.SetInt(num)
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		if num, err := strconv.ParseUint(dec.value, 10, 64); err != nil {
			return newError(fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid unsigned integer number", dec.field, dec.path))
		} else {
			dec.curr.SetUint(num)
		}
	case reflect.Float32, reflect.Float64:
		if num, err := strconv.ParseFloat(dec.value, dec.curr.Type().Bits()); err != nil {
			return newError(fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" should be a valid float number", dec.field, dec.path))
		} else {
			dec.curr.SetFloat(num)
		}
	case reflect.Bool:
		switch dec.value {
		case "true", "on", "1":
			dec.curr.SetBool(true)
		case "false", "off", "0":
			dec.curr.SetBool(false)
		default:
			return newError(fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid boolean", dec.field, dec.path))
		}
	case reflect.Interface:
		dec.curr.Set(reflect.ValueOf(dec.value))
	case reflect.Ptr:
		dec.curr.Set(reflect.New(dec.curr.Type().Elem()))
		dec.curr = dec.curr.Elem()
		return dec.decode()
	case reflect.Struct:
		switch dec.curr.Interface().(type) {
		case time.Time:
			var t time.Time
			var err error
			for _, layout := range timeFormat {
				t, err = time.Parse(layout, dec.value)
				if err == nil {
					break
				}
			}
			if err != nil {
				return newError(fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid datetime", dec.field, dec.path))
			}
			dec.curr.Set(reflect.ValueOf(t))
		case url.URL:
			u, err := url.Parse(dec.value)
			if err != nil {
				return newError(fmt.Errorf("formam: the value of field \"%v\" in path \"%v\" is not a valid url", dec.field, dec.path))
			}
			dec.curr.Set(reflect.ValueOf(*u))
		default:
			/*
				if dec.isKey {
					tmp := dec.curr
					dec.field = dec.value
					if err := dec.begin(); err != nil {
						return err
					}
					dec.curr = tmp
					return nil
				}
			*/
			return newError(fmt.Errorf("formam: not supported type for field \"%v\" in path \"%v\"", dec.field, dec.path))
		}
	default:
		return newError(fmt.Errorf("formam: not supported type for field \"%v\" in path \"%v\"", dec.field, dec.path))
	}

	return nil
}

// findField finds a field by its name, if it is not found,
// then retry the search examining the tag "formam" of every field of struct
func (dec *Decoder) findStructField() error {
	var anon reflect.Value

	num := dec.curr.NumField()
	for i := 0; i < num; i++ {
		field := dec.curr.Type().Field(i)
		if field.Name == dec.field {
			// check if the field's name is equal
			dec.curr = dec.curr.Field(i)
			return nil
		} else if field.Anonymous {
			// if the field is a anonymous struct, then iterate over its fields
			tmp := dec.curr
			dec.curr = dec.curr.FieldByIndex(field.Index)
			if err := dec.findStructField(); err != nil {
				dec.curr = tmp
				continue
			}
			// field in anonymous struct is found,
			// but first it should found the field in the rest of struct
			// (a field with same name in the current struct should have preference over anonymous struct)
			anon = dec.curr
			dec.curr = tmp
		} else if dec.field == field.Tag.Get(dec.opts.TagName) {
			// is not found yet, then retry by its tag name "formam"
			dec.curr = dec.curr.Field(i)
			return nil
		}
	}
	if anon.IsValid() {
		dec.curr = anon
		return nil
	}

	return newError(fmt.Errorf("formam: not found the field \"%v\" in the path \"%v\"", dec.field, dec.path))
}

// expandSlice expands the length and capacity of the current slice
func (dec *Decoder) expandSlice(length int) {
	n := reflect.MakeSlice(dec.curr.Type(), length, length)
	reflect.Copy(n, dec.curr)
	dec.curr.Set(n)
}

// checkCustomType checks if the value to decode has a custom type registered
func (dec *Decoder) checkCustomType() (bool, error) {
	if dec.customTypes == nil {
		return false, nil
	}
	if v, ok := dec.customTypes[dec.curr.Type()]; ok {
		if len(v.fields) > 0 {
			for i := range v.fields {
				if v.fields[i].field.Elem() == dec.curr {
					va, err := v.fields[i].fun(dec.values)
					if err != nil {
						return true, err
					}
					dec.curr.Set(reflect.ValueOf(va))
					return true, nil
				}
			}
			if v.fun != nil {
				va, err := v.fun(dec.values)
				if err != nil {
					return true, err
				}
				dec.curr.Set(reflect.ValueOf(va))
				return true, nil
			}
		} else {
			va, err := v.fun(dec.values)
			if err != nil {
				return true, err
			}
			dec.curr.Set(reflect.ValueOf(va))
			return true, nil
		}
	}
	return false, nil
}
