package querier

import (
	"fmt"
	"reflect"
	"strings"
)

func toStrArrayString(strs ...string) string {
	return fmt.Sprintf("[%s]", strings.Join(strs, ","))
}

// formatValue formats the value to be used in clickhouse query.
func formatValueForCH(v any) string {
	// if it's pointer convert it to a value
	v = getPointerValue(v)

	switch x := v.(type) {
	case uint8, uint16, uint32, uint64, int, int8, int16, int32, int64:
		return fmt.Sprintf("%d", x)
	case float32, float64:
		return fmt.Sprintf("%f", x)
	case string:
		return fmt.Sprintf("'%s'", quoteEscapedString(x))
	case bool:
		return fmt.Sprintf("%v", x)

	case []any:
		if len(x) == 0 {
			return "[]"
		}
		switch x[0].(type) {
		case string:
			strs := []string{}
			for _, sVal := range x {
				strs = append(strs, fmt.Sprintf("'%s'", quoteEscapedString(sVal.(string))))
			}
			return toStrArrayString(strs...)
		case uint8, uint16, uint32, uint64, int, int8, int16, int32, int64, float32, float64, bool:
			return strings.Join(strings.Fields(fmt.Sprint(x)), ",")
		default:
			return toStrArrayString()
		}
	case []string:
		strs := []string{}
		for _, sVal := range x {
			strs = append(strs, fmt.Sprintf("'%s'", quoteEscapedString(sVal)))
		}
		return toStrArrayString(strs...)
	default:
		return ""
	}
}

func getPointerValue(v any) any {

	// Check if the interface value is nil
	if v == nil {
		return nil
	}

	// Use reflection to check if the pointer is nil
	rv := reflect.ValueOf(v)
	if rv.Kind() == reflect.Ptr && rv.IsNil() {
		return nil
	}

	switch x := v.(type) {
	case *uint8:
		if x == nil {
			return nil
		}
		return *x
	case *uint16:
		if x == nil {
			return nil
		}
		return *x
	case *uint32:
		if x == nil {
			return nil
		}
		return *x
	case *uint64:
		if x == nil {
			return nil
		}
		return *x
	case *int:
		if x == nil {
			return nil
		}
		return *x
	case *int8:
		if x == nil {
			return nil
		}
		return *x
	case *int16:
		if x == nil {
			return nil
		}
		return *x
	case *int32:
		if x == nil {
			return nil
		}
		return *x
	case *int64:
		if x == nil {
			return nil
		}
		return *x
	case *float32:
		if x == nil {
			return nil
		}
		return *x
	case *float64:
		if x == nil {
			return nil
		}
		return *x
	case *string:
		if x == nil {
			return nil
		}
		return *x
	case *bool:
		if x == nil {
			return nil
		}
		return *x
	case []any:
		values := []any{}
		for _, val := range x {
			values = append(values, getPointerValue(val))
		}
		return values
	default:
		return v
	}
}

func quoteEscapedString(str string) string {
	// https://clickhouse.com/docs/en/sql-reference/syntax#string
	str = strings.ReplaceAll(str, `\`, `\\`)
	str = strings.ReplaceAll(str, `'`, `\'`)
	return str
}

// formatValueForProm formats the value to be used in promql.
func formatValueForProm(v any) string {
	switch x := v.(type) {
	case int:
		return fmt.Sprintf("%d", x)
	case float32, float64:
		return fmt.Sprintf("%f", x)
	case string:
		return x
	case bool:
		return fmt.Sprintf("%v", x)
	case []interface{}:
		if len(x) == 0 {
			return ""
		}
		switch x[0].(type) {
		case string, int, float32, float64, bool:
			// list of values joined by | for promql - a value can contain whitespace
			var str []string
			for _, sVal := range x {
				str = append(str, fmt.Sprintf("%v", sVal))
			}
			return strings.Join(str, "|")
		default:
			return ""
		}
	default:
		return ""
	}
}
