package odbcmql

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"testing"

	"git.wecise.com/wecise/odb-go/odb"
	"gitee.com/wecisecode/util/cast"
	"gitee.com/wecisecode/util/merrs"
	"github.com/stretchr/testify/assert"
)

func DoActionMatch(t *testing.T, args []any, mql string, rtn *odb.Result, toption *OnErrorOption) (breakup bool, seriouserror bool, err error) {
	if len(args) < 2 {
		s := "match(Kn,Mn) 需要至少两个参数"
		err = merrs.New("%s", s)
		assert.Nil(t, s, err)
		seriouserror = true
		breakup = true
		return
	}
	ks := []string{}
	ms := []any{}
	i := 0
	for ; i+1 < len(args); i += 2 {
		k := strings.TrimSpace(cast.ToString(args[i]))
		ks = append(ks, k)
		var m any
		switch arg := args[i+1].(type) {
		case string:
			if regexp.MustCompile(`^\(\?[^\)]*\).*`).MatchString(arg) {
				r, e := regexp.Compile(arg)
				if e != nil {
					s := "match参数正则表达式错误：" + e.Error()
					err = merrs.New("%s", s)
					assert.Nil(t, s, err)
					seriouserror = true
					breakup = true
					return
				}
				m = r
			}
		case *Action:
			switch arg.Name {
			case "<FUZZY>":
				m = arg
			}
		}
		if m == nil {
			m = fmt.Sprintf("%#v", args[i+1])
		}
		ms = append(ms, m)
	}
	if i > len(args) {
		err = merrs.New("match(Kn,Mn) 参数 Kn,Mn 需要成对出现")
		assert.Nil(t, "参数错误", err)
		seriouserror = true
		breakup = true
		return
	}
	x, matchingvalues := MatchValues(rtn, ks, ms)
	if x == 0 {
		if len(rtn.Data) == 0 {
			err = merrs.New("%s", "没有找到记录与期望值不符")
		} else {
			argsbs, _ := json.MarshalIndent(args, "", "  ")
			matchingbs, _ := json.MarshalIndent(matchingvalues, "", "  ")
			err = merrs.NewError(fmt.Sprint("共", len(rtn.Data), "记录，没有找到记录与期望值不符"),
				merrs.SSMaps{{"mql": mql},
					{"match args": fmt.Sprint(string(argsbs))},
					{"lastmatchingvalues": string(matchingbs)}})
		}
		if toption.regex[OnErrorBreak] != nil && toption.regex[OnErrorBreak].MatchString(err.Error()) {
			if toption.regex[OnErrorBreak] == matchall && !toption.noerrorinfo {
				logger.Info("error:", err)
			}
			breakup = true
			return
		}
		if toption.regex[OnErrorIgnore] != nil && toption.regex[OnErrorIgnore].MatchString(err.Error()) {
			// 直接输出错误信息，不返回错误信息，不中断循环， 不在 testing.T 中报告错误，不中断测试
			if toption.regex[OnErrorIgnore] == matchall && !toption.noerrorinfo {
				logger.Info("pass_with_error:", err)
			}
			err = nil
			breakup = true
			return
		}
		if !assert.Nil(t, "error", err) {
			// 不直接输出错误信息，返回错误信息，中断循环， 在 testing.T 中报告错误，中断测试
			breakup = true
			return
		}
	}
	return
}

func DoActionEqual(t *testing.T, toption *OnErrorOption, result *odb.Result, args ...string) (breakup bool, seriouserror bool, err error) {
	if len(args) < 3 {
		err = merrs.New("%s", "equal(N,F,V) 需要三个参数")
		assert.Nil(t, "参数错误", err)
		seriouserror = true
		breakup = true
		return
	}
	n := cast.ToInt(strings.TrimSpace(args[0]))
	for kvi := 1; kvi+1 < len(args); kvi += 2 {
		field := strings.TrimSpace(args[kvi])
		value := strings.TrimSpace(args[kvi+1])
		if len(value) > 1 && value[0] == '"' && value[len(value)-1] == '"' {
			x, e := strconv.Unquote(value)
			if e != nil {
				err = merrs.New("%s V:%s", "equal(N,F,V) 参数 V 格式错误", value)
				assert.Nil(t, "参数错误", err)
				seriouserror = true
				breakup = true
				return
			}
			value = x
		}
		if n < 0 && result.Meta != nil {
			dat := result.Meta
			breakup, seriouserror, err = DeepEqual(t, toption, dat, field, value)
			if breakup {
				return
			}
		} else if len(result.Data) > n && result.Data[n] != nil {
			dat := result.Data[n]
			breakup, seriouserror, err = DeepEqual(t, toption, dat, field, value)
			if breakup {
				return
			}
		} else {
			err = merrs.New("%s", fmt.Sprint("字段 ", field, " 实际值(不存在)与期望值(", value, ")不符"))
			if toption.regex[OnErrorBreak] != nil && toption.regex[OnErrorBreak].MatchString(err.Error()) {
				if toption.regex[OnErrorBreak] == matchall && !toption.noerrorinfo {
					logger.Info("error:", err)
				}
				breakup = true
				return
			}
			if toption.regex[OnErrorIgnore] != nil && toption.regex[OnErrorIgnore].MatchString(err.Error()) {
				// 直接输出错误信息，不返回错误信息，不中断循环， 不在 testing.T 中报告错误，不中断测试
				if toption.regex[OnErrorIgnore] == matchall && !toption.noerrorinfo {
					logger.Info("pass_with_error:", err)
				}
				err = nil
				return
			}
			if !assert.Equal(t, value, "", err) {
				// 不直接输出错误信息，返回错误信息，中断循环， 在 testing.T 中报告错误，中断测试
				return
			}
		}
	}
	return
}

func DeepEqual(t *testing.T, toption *OnErrorOption, dat map[string]any, field, value string) (breakup bool, seriouserror bool, err error) {
	kks := strings.Split(field, ".")
	kki := len(kks)
	v := ""
	var vv any
	for ; vv == nil && kki >= 1; kki-- {
		kk := strings.Join(kks[:kki], ".")
		vv = dat[kk]
		for ; vv != nil && kki < len(kks); kki++ {
			switch mv := vv.(type) {
			case map[string]any:
				vv = mv[kks[kki]]
				if vv == nil {
					if kks[kki] == "len" {
						vv = len(mv)
					}
				}
			case map[string][]string:
				vvs := mv[kks[kki]]
				if vvs == nil {
					if kks[kki] == "len" {
						vv = len(mv)
					}
				} else {
					vv = vvs
				}
			case map[string][]any:
				vvs := mv[kks[kki]]
				if vvs == nil {
					if kks[kki] == "len" {
						vv = len(mv)
					}
				} else {
					vv = vvs
				}
			case []any:
				if kks[kki] == "len" {
					vv = len(mv)
				} else {
					sik := strings.TrimSpace(kks[kki])
					ik, e := strconv.Atoi(sik)
					if e != nil {
						err = merrs.New("%s V:%s", "equal(N,F,V) 参数 V 格式错误", field)
						assert.Nil(t, "参数错误", err)
						seriouserror = true
						breakup = true
						return
					}
					vv = mv[ik]
				}
			case []string:
				if kks[kki] == "len" {
					vv = len(mv)
				} else {
					sik := strings.TrimSpace(kks[kki])
					ik, e := strconv.Atoi(sik)
					if e != nil {
						err = merrs.New("%s V:%s", "equal(N,F,V) 参数 V 格式错误", field)
						assert.Nil(t, "参数错误", err)
						seriouserror = true
						breakup = true
						return
					}
					vv = mv[ik]
				}
			}
		}
	}
	v = fmt.Sprint(vv)
	if v != value {
		err = merrs.New("%s", fmt.Sprint("字段 ", field, " 实际值(", v, ")与期望值(", value, ")不符"))
		if toption.regex[OnErrorBreak] != nil && toption.regex[OnErrorBreak].MatchString(err.Error()) {
			if toption.regex[OnErrorBreak] == matchall && !toption.noerrorinfo {
				logger.Info("error:", err)
			}
			breakup = true
			return
		}
		if toption.regex[OnErrorIgnore] != nil && toption.regex[OnErrorIgnore].MatchString(err.Error()) {
			// 直接输出错误信息，不返回错误信息，不中断循环， 不在 testing.T 中报告错误，不中断测试
			if toption.regex[OnErrorIgnore] == matchall && !toption.noerrorinfo {
				logger.Info("pass_with_error:", err)
			}
			err = nil
			return
		}
		if !assert.Equal(t, value, v, err) {
			breakup = true
			return
		}
	}
	return
}
