package reflect

import (
	"fmt"
	"reflect"
	"regexp"
	"strings"

	"git.ma-al.com/goc_daniel/b2b/app/db"
)

// TODO: instead of matching with string.Contains use something less error-prone
func checkIfContainsJSON(i int, t reflect.Type, name string) string {
	if wholeTag, ok := t.Field(i).Tag.Lookup("json"); ok {
		tags := strings.Split(wholeTag, ",")
		for _, tag := range tags {
			if name == strings.TrimSpace(tag) {
				return db.DB.NamingStrategy.ColumnName(t.Name(), t.Field(i).Name)
			}
		}
	}
	return ""
}

// Not tail recursive but should do fine. Goes only as deep as the hierarchy of
// inlined structs.
// TODO: improve used internally checkIfContainsJSON
func GetGormColumnFromJsonField(jsonName string, t reflect.Type) (string, error) {
	var res string
	for i := range make([]bool, t.NumField()) {
		if tag, ok := t.Field(i).Tag.Lookup("json"); ok && strings.Contains(tag, "inline") {
			var err error
			res, err = GetGormColumnFromJsonField(jsonName, t.Field(i).Type)
			if err != nil {
				return "", fmt.Errorf("no field of struct %q has a name %q in its json form", t.Name(), jsonName)
			}

		} else {
			res = checkIfContainsJSON(i, t, jsonName)
		}
		if res != "" {
			return res, nil
		}
	}
	return "", fmt.Errorf("no field of struct %q has a name %q in its json form", t.Name(), jsonName)
}

func GetTableName[T any]() string {
	var model T
	typ := reflect.TypeOf(model).Name()
	return db.DB.NamingStrategy.TableName(typ)
}

func GetParamFromFieldTag[T any](object T, fieldname string, tagname string, paramname string) string {
	if table, ok := reflect.TypeOf(object).FieldByName(fieldname); ok {
		if t, ok := table.Tag.Lookup(tagname); ok {
			if paramname == "" {
				return t
			}
			re := regexp.MustCompile(`(?m)` + paramname + `:(\w*)`)
			f := re.FindAllStringSubmatch(t, -1)
			if len(re.FindAllStringSubmatch(t, -1)) > 0 {
				return f[0][1]
			}
		}
	}
	return ""
}

func GetPrimaryKey[T any](item T) string {
	var search func(T) string = func(item T) string {
		val := reflect.ValueOf(item)
		typ := reflect.TypeOf(item)
		for i := 0; i < val.NumField(); i++ {
			if gg, ok := typ.Field(i).Tag.Lookup("gorm"); ok {
				xx := strings.Split(gg, ";")
				for _, t := range xx {
					if strings.HasPrefix(strings.ToLower(t), "primarykey") {
						return db.DB.NamingStrategy.TableName(typ.Field(i).Name)
					}
				}
			}
			if val.Field(i).Type().String() == "db.Model" {
				return db.DB.NamingStrategy.TableName("ID")
			}
		}
		return ""
	}
	return search(item)
}
