package exp

import (
	"reflect"
	"sort"

	"github.com/doug-martin/goqu/v8/internal/errors"
	"github.com/doug-martin/goqu/v8/internal/util"
)

type (
	update struct {
		col IdentifierExpression
		val interface{}
	}
)

func set(col IdentifierExpression, val interface{}) UpdateExpression {
	return update{col: col, val: val}
}

func NewUpdateExpressions(update interface{}) (updates []UpdateExpression, err error) {
	if u, ok := update.(UpdateExpression); ok {
		updates = append(updates, u)
		return updates, nil
	}
	updateValue := reflect.Indirect(reflect.ValueOf(update))
	switch updateValue.Kind() {
	case reflect.Map:
		keys := util.ValueSlice(updateValue.MapKeys())
		sort.Sort(keys)
		for _, key := range keys {
			updates = append(updates, ParseIdentifier(key.String()).Set(updateValue.MapIndex(key).Interface()))
		}
	case reflect.Struct:
		return getUpdateExpressionsStruct(updateValue)
	default:
		return nil, errors.New("unsupported update interface type %+v", updateValue.Type())
	}
	return updates, nil
}

func getUpdateExpressionsStruct(value reflect.Value) (updates []UpdateExpression, err error) {
	r, err := NewRecordFromStruct(value.Interface(), false, true)
	if err != nil {
		return updates, err
	}
	cols := r.Cols()
	for _, col := range cols {
		updates = append(updates, ParseIdentifier(col).Set(r[col]))
	}
	return updates, nil
}

func (u update) Expression() Expression {
	return u
}

func (u update) Clone() Expression {
	return update{col: u.col.Clone().(IdentifierExpression), val: u.val}
}

func (u update) Col() IdentifierExpression {
	return u.col
}

func (u update) Val() interface{} {
	return u.val
}
