package waf

import (
	"encoding/json"
	"github.com/TeaWeb/code/teaconfigs"
	"github.com/TeaWeb/code/teawaf"
	"github.com/TeaWeb/code/teawaf/checkpoints"
	"github.com/TeaWeb/code/teaweb/actions/default/proxy/proxyutils"
	"github.com/TeaWeb/code/teaweb/actions/default/proxy/waf/wafutils"
	"github.com/iwind/TeaGo/actions"
	"github.com/iwind/TeaGo/lists"
	"github.com/iwind/TeaGo/logs"
	"github.com/iwind/TeaGo/maps"
	"strings"
)

type RuleAddAction actions.Action

func (this *RuleAddAction) RunGet(params struct {
	WafId   string
	GroupId string
}) {
	waf := teaconfigs.SharedWAFList().FindWAF(params.WafId)
	if waf == nil {
		this.Fail("找不到WAF")
	}
	this.Data["config"] = maps.Map{
		"id":            waf.Id,
		"name":          waf.Name,
		"countInbound":  waf.CountInboundRuleSets(),
		"countOutbound": waf.CountOutboundRuleSets(),
		"inbound":       waf.Inbound,
	}

	group := waf.FindRuleGroup(params.GroupId)
	if group == nil {
		this.Fail("找不到分组")
	}

	this.Data["inbound"] = group.IsInbound
	this.Data["outbound"] = !group.IsInbound

	this.Data["group"] = group
	this.Data["connectors"] = []maps.Map{
		{
			"name":        "和(AND)",
			"value":       teawaf.RuleConnectorAnd,
			"description": "所有规则都满足才视为匹配",
		},
		{
			"name":        "或(OR)",
			"value":       teawaf.RuleConnectorOr,
			"description": "任一规则满足了就视为匹配",
		},
	}

	// check points
	checkpointList := []maps.Map{}
	for _, def := range checkpoints.AllCheckpoints {
		if (group.IsInbound && def.Instance.IsRequest()) || (!group.IsInbound && !def.Instance.IsRequest()) {
			checkpointList = append(checkpointList, maps.Map{
				"name":         def.Name,
				"prefix":       def.Prefix,
				"description":  def.Description,
				"hasParams":    def.HasParams,
				"paramOptions": def.Instance.ParamOptions(),
				"options": lists.Map(def.Instance.Options(), func(k int, v interface{}) interface{} {
					{
						option, ok := v.(*checkpoints.FieldOption)
						if ok {
							return maps.Map{
								"type":        option.Type(),
								"name":        option.Name,
								"maxLength":   option.MaxLength,
								"code":        option.Code,
								"rightLabel":  option.RightLabel,
								"value":       option.Value,
								"isRequired":  option.IsRequired,
								"size":        option.Size,
								"comment":     option.Comment,
								"placeholder": option.Placeholder,
							}
						}
					}

					{
						option, ok := v.(*checkpoints.OptionsOption)
						if ok {
							return maps.Map{
								"type":       option.Type(),
								"name":       option.Name,
								"code":       option.Code,
								"rightLabel": option.RightLabel,
								"value":      option.Value,
								"isRequired": option.IsRequired,
								"size":       option.Size,
								"comment":    option.Comment,
								"options":    option.Options,
							}
						}
					}

					return maps.Map{}
				}),
			})
		}
	}

	this.Data["checkpoints"] = checkpointList

	this.Data["operators"] = lists.Map(teawaf.AllRuleOperators, func(k int, v interface{}) interface{} {
		def := v.(*teawaf.RuleOperatorDefinition)
		return maps.Map{
			"name":        def.Name,
			"code":        def.Code,
			"description": def.Description,
			"case":        def.CaseInsensitive,
		}
	})

	this.Data["actions"] = lists.Map(teawaf.AllActions, func(k int, v interface{}) interface{} {
		def := v.(*teawaf.ActionDefinition)
		return maps.Map{
			"name":        def.Name,
			"description": def.Description,
			"code":        def.Code,
		}
	})

	this.Show()
}

// 提交测试或者保存
func (this *RuleAddAction) RunPost(params struct {
	WafId   string
	GroupId string

	Name string

	RulePrefixes  []string
	RuleParams    []string
	RuleOperators []string
	RuleValues    []string
	RuleCases     []int
	RuleOptions   []string

	Connector string
	Action    string

	Test         bool
	TestPrefixes []string
	TestParams   []string
	TestValues   []string

	Must *actions.Must
}) {

	set := teawaf.NewRuleSet()
	set.Name = params.Name
	for index, prefix := range params.RulePrefixes {
		if index < len(params.RuleParams) && index < len(params.RuleOperators) && index < len(params.RuleValues) && index < len(params.RuleCases) && index < len(params.RuleOptions) {
			rule := teawaf.NewRule()
			rule.Operator = params.RuleOperators[index]

			param := params.RuleParams[index]
			if len(param) > 0 {
				rule.Param = "${" + prefix + "." + param + "}"
			} else {
				rule.Param = "${" + prefix + "}"
			}
			rule.Value = params.RuleValues[index]
			rule.IsCaseInsensitive = params.RuleCases[index] == 1

			// 选项
			options := params.RuleOptions[index]
			if len(options) > 0 {
				arr := []maps.Map{}
				err := json.Unmarshal([]byte(options), &arr)
				if err != nil {
					logs.Error(err)
				} else {
					rule.CheckpointOptions = map[string]string{}
					for _, m := range arr {
						code := m.GetString("code")
						value := m.GetString("value")
						rule.CheckpointOptions[code] = value
					}
				}
			}

			// 校验
			err := rule.Init()
			if err != nil {
				this.Fail("校验规则 '" + rule.Param + " " + rule.Operator + " " + rule.Value + "' 失败，原因：" + err.Error())
			}

			set.AddRule(rule)
		}
	}

	set.Connector = params.Connector

	// action
	set.Action = params.Action
	set.ActionOptions = maps.Map{}
	for k, v := range this.ParamsMap {
		if len(v) == 0 {
			continue
		}
		index := strings.Index(k, "action_")
		if index > -1 {
			set.ActionOptions[k[len("action_"):]] = v[0]
		}
	}

	// 测试
	if params.Test {
		err := set.Init()
		if err != nil {
			this.Fail("校验错误：" + err.Error())
		}

		matchedIndex := -1
		breakIndex := -1
		matchLogs := []string{"start matching ...", "==="}
	Loop:
		for index, prefix := range params.TestPrefixes {
			if index < len(params.TestParams) && index < len(params.TestValues) {
				param := ""
				if len(params.TestParams[index]) == 0 {
					param = "${" + prefix + "}"
				} else {
					param = "${" + prefix + "." + params.TestParams[index] + "}"
				}

				breakIndex = index

				for _, rule := range set.Rules {
					if rule.Param == param {
						value := params.TestValues[index]
						if rule.Test(value) {
							matchLogs = append(matchLogs, "rule: "+rule.Param+" "+rule.Operator+" "+rule.Value+"\ncompare: "+value+"\nresult:true")

							if set.Connector == teawaf.RuleConnectorOr {
								matchedIndex = index
								break Loop
							}

							if set.Connector == teawaf.RuleConnectorAnd {
								matchedIndex = index
							}
						} else {
							matchLogs = append(matchLogs, "rule: "+rule.Param+" "+rule.Operator+" "+rule.Value+"\ncompare: "+value+"\nresult:false")

							if set.Connector == teawaf.RuleConnectorAnd {
								matchedIndex = -1
								break Loop
							}
						}
					}
				}
			}
		}

		this.Data["matchedIndex"] = matchedIndex
		this.Data["breakIndex"] = breakIndex
		this.Data["matchLogs"] = matchLogs
		this.Success()
	}

	// 保存
	params.Must.
		Field("name", params.Name).
		Require("请输入规则集名称")

	// 检查设置
	err := set.Init()
	if err != nil {
		this.Fail("规则校验失败：" + err.Error())
	}

	// waf
	wafList := teaconfigs.SharedWAFList()
	waf := wafList.FindWAF(params.WafId)
	if waf == nil {
		this.Fail("找不到WAF")
	}

	group := waf.FindRuleGroup(params.GroupId)
	if group == nil {
		this.Fail("找不到Group")
	}

	group.AddRuleSet(set)
	err = wafList.SaveWAF(waf)
	if err != nil {
		this.Fail("保存失败：" + err.Error())
	}

	// 通知刷新
	if wafutils.IsPolicyUsed(waf.Id) {
		proxyutils.NotifyChange()
	}

	this.Success()
}
