package rest

import (
	"fmt"
	"reflect"

	"git.ali33.ru/fcg-xvii/go-tools/json"
)

type OrderType byte

func parseOrder(order json.Map, parent string) (IOrderObject, error) {
	parentName := func(name string) string {
		if len(parent) > 0 {
			return fmt.Sprintf("%s.%s", parent, name)
		}
		return name
	}
	obj := &_orderObject{
		orders: make(map[string]IOrder),
	}
	for k, v := range order {
		rv := reflect.ValueOf(v)
		switch rv.Kind() {
		case reflect.Bool:
			obj.orders[k] = &_orderField{
				fieldName: k,
				isAsc:     v.(bool),
			}
		case reflect.Map:
			t := reflect.TypeOf(order)
			if rv.CanConvert(t) {
				childRV := rv.Convert(t)
				child, err := parseOrder(childRV.Interface().(json.Map), parentName(k))
				if err != nil {
					return nil, err
				}
				obj.orders[k] = child
			}
		default:
			return nil, fmt.Errorf("unexpected type in [%v]", parentName(k))
		}
	}
	return obj, nil
}

const (
	OrderField OrderType = iota
	OrderObject
)

////////////////////////////////////////////

type IOrder interface {
	Type() OrderType
}

type IOrderField interface {
	IOrder
	FieldName() string
	IsAsc() bool
}

type IOrderObject interface {
	IOrder
	MapOrder() map[string]IOrder
	MapFields() map[string]IOrderField
	MapObjects() map[string]IOrderObject
}

////////////////////////////////////////////

type _orderField struct {
	fieldName string
	isAsc     bool
}

func (s *_orderField) FieldName() string {
	return s.fieldName
}

func (s *_orderField) IsAsc() bool {
	return s.isAsc
}

func (s *_orderField) Type() OrderType {
	return OrderField
}

func (s *_orderField) String() string {
	return fmt.Sprintf("%v: %v\n", s.fieldName, s.isAsc)
}

////////////////////////////////////////////

type _orderObject struct {
	orders map[string]IOrder
}

func (s *_orderObject) Type() OrderType {
	return OrderObject
}

func (s *_orderObject) MapOrder() map[string]IOrder {
	return s.orders
}

func (s *_orderObject) MapFields() map[string]IOrderField {
	res := make(map[string]IOrderField)
	for k, v := range s.orders {
		if v.Type() == OrderField {
			res[k] = v.(IOrderField)
		}
	}
	return res
}

func (s *_orderObject) MapObjects() map[string]IOrderObject {
	res := make(map[string]IOrderObject)
	for k, v := range s.orders {
		if v.Type() == OrderObject {
			res[k] = v.(IOrderObject)
		}
	}
	return res
}

func (s *_orderObject) String() string {
	res := "{\n"
	for k, v := range s.orders {
		res += fmt.Sprintf("\t%s: %v", k, v)
	}
	res += "}\n"
	return res
}