package db

import (
	"fmt"
	"reflect"

	"github.com/iancoleman/strcase"
	"gorm.io/gorm"
	"reichard.io/imagini/graph/model"
)

// Generic function used to generate filters for the DB
func (dbm *DBManager) generateFilter(tx *gorm.DB, filter interface{}) *gorm.DB {
	ptr := reflect.ValueOf(filter)
	v := reflect.Indirect(ptr)

	if v == reflect.ValueOf(nil) {
		return tx
	}

	for i := 0; i < v.NumField(); i++ {
		fieldName := strcase.ToSnake(v.Type().Field(i).Name)
		fieldVal := v.Field(i)

		if fieldVal.IsNil() {
			continue
		}

		switch valType := fieldVal.Type(); valType {
		case reflect.TypeOf(&model.StringFilter{}):
			tx = generateStringFilter(tx, fieldName, fieldVal.Interface().(*model.StringFilter))
		case reflect.TypeOf(&model.BooleanFilter{}):
			tx = generateBooleanFilter(tx, fieldName, fieldVal.Interface().(*model.BooleanFilter))
		case reflect.TypeOf(&model.FloatFilter{}):
			tx = generateFloatFilter(tx, fieldName, fieldVal.Interface().(*model.FloatFilter))
		case reflect.TypeOf(&model.IntFilter{}):
			tx = generateIntFilter(tx, fieldName, fieldVal.Interface().(*model.IntFilter))
		case reflect.TypeOf(&model.IDFilter{}):
			tx = generateIDFilter(tx, fieldName, fieldVal.Interface().(*model.IDFilter))
		case reflect.TypeOf(&model.TimeFilter{}):
			tx = generateTimeFilter(tx, fieldName, fieldVal.Interface().(*model.TimeFilter))
		case reflect.TypeOf(&model.RoleFilter{}):
			tx = generateRoleFilter(tx, fieldName, fieldVal.Interface().(*model.RoleFilter))
		case reflect.TypeOf(&model.DeviceTypeFilter{}):
			tx = generateDeviceTypeFilter(tx, fieldName, fieldVal.Interface().(*model.DeviceTypeFilter))
		case reflect.TypeOf(&model.AuthTypeFilter{}):
			tx = generateAuthTypeFilter(tx, fieldName, fieldVal.Interface().(*model.AuthTypeFilter))
		}
	}

	return tx
}

func generateStringFilter(tx *gorm.DB, fieldName string, filter *model.StringFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	if filter.StartsWith != nil {
		tx = tx.Where(fmt.Sprintf("%s LIKE ?", fieldName), fmt.Sprintf("%s%%", *filter.StartsWith))
	}
	if filter.NotStartsWith != nil {
		tx = tx.Where(fmt.Sprintf("%s NOT LIKE ?", fieldName), fmt.Sprintf("%s%%", *filter.NotStartsWith))
	}
	if filter.EndsWith != nil {
		tx = tx.Where(fmt.Sprintf("%s LIKE ?", fieldName), fmt.Sprintf("%%%s", *filter.EndsWith))
	}
	if filter.NotEndsWith != nil {
		tx = tx.Where(fmt.Sprintf("%s NOT LIKE ?", fieldName), fmt.Sprintf("%%%s", *filter.NotEndsWith))
	}
	if filter.Contains != nil {
		tx = tx.Where(fmt.Sprintf("%s LIKE ?", fieldName), fmt.Sprintf("%%%s%%", *filter.Contains))
	}
	if filter.NotContains != nil {
		tx = tx.Where(fmt.Sprintf("%s NOT LIKE ?", fieldName), fmt.Sprintf("%%%s%%", *filter.NotContains))
	}
	return tx
}

func generateBooleanFilter(tx *gorm.DB, fieldName string, filter *model.BooleanFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	return tx
}

func generateFloatFilter(tx *gorm.DB, fieldName string, filter *model.FloatFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	if filter.GreaterThan != nil {
		tx = tx.Where(fmt.Sprintf("%s > ?", fieldName), *filter.GreaterThan)
	}
	if filter.GreaterThanOrEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s >= ?", fieldName), *filter.GreaterThanOrEqualTo)
	}
	if filter.LessThan != nil {
		tx = tx.Where(fmt.Sprintf("%s < ?", fieldName), *filter.LessThan)
	}
	if filter.LessThanOrEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s <= ?", fieldName), *filter.LessThanOrEqualTo)
	}
	return tx
}

func generateIntFilter(tx *gorm.DB, fieldName string, filter *model.IntFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	if filter.GreaterThan != nil {
		tx = tx.Where(fmt.Sprintf("%s > ?", fieldName), *filter.GreaterThan)
	}
	if filter.GreaterThanOrEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s >= ?", fieldName), *filter.GreaterThanOrEqualTo)
	}
	if filter.LessThan != nil {
		tx = tx.Where(fmt.Sprintf("%s < ?", fieldName), *filter.LessThan)
	}
	if filter.LessThanOrEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s <= ?", fieldName), *filter.LessThanOrEqualTo)
	}
	return tx
}

func generateIDFilter(tx *gorm.DB, fieldName string, filter *model.IDFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	return tx
}

func generateTimeFilter(tx *gorm.DB, fieldName string, filter *model.TimeFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	if filter.GreaterThan != nil {
		tx = tx.Where(fmt.Sprintf("%s > ?", fieldName), *filter.GreaterThan)
	}
	if filter.GreaterThanOrEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s >= ?", fieldName), *filter.GreaterThanOrEqualTo)
	}
	if filter.LessThan != nil {
		tx = tx.Where(fmt.Sprintf("%s < ?", fieldName), *filter.LessThan)
	}
	if filter.LessThanOrEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s <= ?", fieldName), *filter.LessThanOrEqualTo)
	}
	return tx
}

func generateRoleFilter(tx *gorm.DB, fieldName string, filter *model.RoleFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	return tx
}

func generateDeviceTypeFilter(tx *gorm.DB, fieldName string, filter *model.DeviceTypeFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	return tx
}

func generateAuthTypeFilter(tx *gorm.DB, fieldName string, filter *model.AuthTypeFilter) *gorm.DB {
	if filter.EqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s == ?", fieldName), *filter.EqualTo)
	}
	if filter.NotEqualTo != nil {
		tx = tx.Where(fmt.Sprintf("%s != ?", fieldName), *filter.NotEqualTo)
	}
	return tx
}
