package autogencase

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strings"

	"github.com/tidwall/gjson"
)

type placeHolderLookup map[string]string // this placeHolderLookup is used for fuzzy e2e

func (lookup placeHolderLookup) buildPathParams(oldPath string, params map[string]string) string {
	p := oldPath
	for k, v := range params {
		realworldVal := v
		if v == Placehodler {
			if rv, ok := lookup[k]; ok {
				realworldVal = rv
			}
		}
		p = strings.Replace(p, fmt.Sprintf("{%s}", k), realworldVal, 1)
	}
	return p
}

func (lookup placeHolderLookup) fillQueryParams(query *url.Values, params *gjson.Result) error {
	var err error
	params.ForEach(func(key, value gjson.Result) bool {
		switch value.Type {
		case gjson.Null:
			// or may be "" as null
			query.Add(key.String(), "null")
		case gjson.False, gjson.True, gjson.Number:
			query.Add(key.String(), value.String())
		case gjson.String:
			realworldVal := value.String()
			if v, ok := lookup[key.String()]; ok {
				realworldVal = v
			}
			query.Add(key.String(), realworldVal)
		case gjson.JSON:
			if value.IsObject() {
				if value.String() != "{}" {
					err = fmt.Errorf("found unspported vaule for key %v in query_params", key.String())
					return false
				}
				query.Add(key.String(), value.String())
			}
			if value.IsArray() {
				array := value.Array()
				for _, item := range array {
					query.Add(key.String(), item.String())
				}
			}
		}
		return true // keep iterating
	})
	return err
}

func (lookup placeHolderLookup) fillBody(out map[string]any, data *gjson.Result) {
	data.ForEach(func(key, value gjson.Result) bool {
		k := key.String()
		switch value.Type {
		case gjson.Null:
			out[k] = json.RawMessage(value.Raw)
		case gjson.False, gjson.True:
			out[k] = value.Bool()
		case gjson.Number:
			out[k] = value.Int()
		case gjson.String:
			// replace placeholder
			realworldVal := value.String()
			if value.String() == Placehodler {
				realworldVal = lookup[k]
			}
			out[k] = realworldVal
		case gjson.JSON:
			if value.IsArray() {
				array := make([]any, 0)
				for _, item := range value.Array() {
					if !item.IsObject() {
						array = append(array, json.RawMessage(item.Raw))
					} else {
						m := make(map[string]any)
						lookup.fillBody(m, &item)
						array = append(array, m)
					}
				}
				out[k] = array
			} else if value.IsObject() {
				m := make(map[string]any)
				lookup.fillBody(m, &value)
				out[k] = m
			}
		}
		return true // keep iterating
	})
}
