package rest_gorm

import (
	"fmt"
	"log"
	"reflect"
	"strings"

	"git.ali33.ru/fcg-xvii/rest"
	"gorm.io/gorm"
)

func Preload(obj any, fields rest.FieldList, q *gorm.DB) (*gorm.DB, error) {
	return preloadPrefix(obj, fields, q, "")
}

func preloadPrefix(obj any, fields rest.FieldList, q *gorm.DB, prefix string) (*gorm.DB, error) {
	val := reflect.ValueOf(obj)
	names, err := namesExt(val)
	if err != nil {
		return nil, err
	}
	for _, name := range names {
		if f, check := fields.Field(name.eName); check {
			log.Println("preload", prefix+name.eName)
			q = q.Preload(prefix + name.eName)
			if f.IsObject() {
				log.Println("is_object:" + prefix + name.eName)
				if q, err = preloadPrefix(name.eValue, f.Names, q, prefix+name.eName+"."); err != nil {
					return nil, err
				}
			}
		}
	}
	return q, nil
}

type eExtName struct {
	eName  string
	eValue any
}

func fElemType(t reflect.Type) reflect.Type {
	switch t.Kind() {
	case reflect.Ptr, reflect.Interface, reflect.Slice:
		return fElemType(t.Elem())
	}
	return t
}

func namesExt(val reflect.Value) (res []eExtName, err error) {

	k := val.Kind()
	switch k {
	case reflect.Ptr, reflect.Interface, reflect.Slice:
		return namesExt(val.Elem())
	}
	if k != reflect.Struct {
		return nil, fmt.Errorf("type [ %v ] is not a struct", k)
	}
	t := reflect.TypeOf(val.Interface())
	for i := 0; i < t.NumField(); i++ {
		fType := t.Field(i)
		tag := fType.Tag.Get("rest")
		if len(tag) > 0 && strings.Contains(tag, "ext") {
			rVal := reflect.New(fElemType(fType.Type))
			res = append(
				res,
				eExtName{
					eName:  fType.Name,
					eValue: rVal.Interface(),
				},
			)
		}
	}
	return
}