package rule

import (
	"encoding/json"
	"git.sxidc.com/go-framework/baize/convenient/domain/query_rule/definition"
	"git.sxidc.com/go-framework/baize/framework/core/domain"
	"git.sxidc.com/go-framework/baize/framework/core/infrastructure"
	"git.sxidc.com/go-framework/baize/framework/core/infrastructure/database"
	"git.sxidc.com/go-framework/baize/framework/core/infrastructure/database/clause"
	"git.sxidc.com/go-framework/baize/framework/core/infrastructure/database/sql"
	"git.sxidc.com/go-framework/baize/framework/core/tag/rule"
	"git.sxidc.com/go-tools/utils/strutils"
	"github.com/pkg/errors"
)

const (
	LogicalOperatorAnd = "and"
	LogicalOperatorOr  = "or"
)

func isSupportedLogicalOperator(logicalOperator string) bool {
	return logicalOperator == LogicalOperatorAnd || logicalOperator == LogicalOperatorOr
}

// Rule 规则二叉树
type Rule struct {
	LogicalOperator string `json:"logicalOperator"`
	Left            *Rule  `json:"left"`
	Right           *Rule  `json:"right"`

	FieldName string `json:"fieldName"`
	FieldType string `json:"fieldType"`
	Operator  string `json:"operator"`
	Value     any    `json:"value"`
}

func (r Rule) check() error {
	if r.isNode() {
		return r.checkAsNode()
	} else {
		return r.checkAsLeaf()
	}
}

func (r Rule) checkAsNode() error {
	if !isSupportedLogicalOperator(r.LogicalOperator) {
		return errors.New("不支持的逻辑操作符")
	}

	if r.Left == nil {
		return errors.New("左结点为空")
	}

	if r.Right == nil {
		return errors.New("右结点为空")
	}

	if strutils.IsStringNotEmpty(r.Left.LogicalOperator) {
		err := r.Left.checkAsNode()
		if err != nil {
			return err
		}
	} else {
		err := r.Left.checkAsLeaf()
		if err != nil {
			return err
		}
	}

	if strutils.IsStringNotEmpty(r.Right.LogicalOperator) {
		err := r.Right.checkAsNode()
		if err != nil {
			return err
		}
	} else {
		err := r.Right.checkAsLeaf()
		if err != nil {
			return err
		}
	}

	return nil
}

func (r Rule) checkAsLeaf() error {
	if strutils.IsStringEmpty(r.FieldName) {
		return errors.New("字段名为空")
	}

	if !rule.IsSupportedType(r.FieldType) {
		return errors.New("字段类型不支持")
	}

	if !definition.IsSupportedOperator(r.Operator) {
		return errors.New("操作符不支持")
	}

	return nil
}

func (r Rule) isNode() bool {
	return strutils.IsStringNotEmpty(r.LogicalOperator)
}

// HasRule 检查是否存在规则
// 参数:
// - dbSchema: 数据库schema
// - scope: 范围
// - domainName: 领域名称
// - i: 基础设施
// 返回值:
// - 是否存在规则
// - 错误
func HasRule(dbSchema string, scope string, domainName string, i *infrastructure.Infrastructure) (bool, error) {
	dbExecutor := i.DBExecutor()

	return database.CheckExist(dbExecutor, &sql.CheckExistExecuteParams{
		TableName: domain.TableName(dbSchema, &Entity{}),
		Conditions: sql.NewConditions().
			Equal(ColumnScope, scope).
			Equal(ColumnDomainName, domainName).
			Equal(ColumnEnabled, true),
	})
}

// FormConditionClause 获取规则并返回条件语句
// 参数:
// - dbSchema: 数据库schema
// - scope: 范围
// - domainName: 领域名称
// - i: 基础设施
// - ruleParams: 规则参数
// 返回值:
// - 语句接口,当错误为nil时,语句接口也可能是nil,代表该规则并没有被赋值,所以是没有条件的
// - 错误
func FormConditionClause(dbSchema string, scope string, domainName string, i *infrastructure.Infrastructure, ruleParams map[string]any) (clause.Clause, error) {
	r, err := getEnabledRule(dbSchema, scope, domainName, i)
	if err != nil {
		return nil, err
	}

	c, err := formConditionClause(domainName, r, ruleParams)
	if err != nil {
		return nil, err
	}

	return c, nil
}

// FormConditionClauseByRule  使用Rule结构构造并返回条件语句
// 参数:
// - domainName: 领域名称
// - rule: Rule的结构
// - ruleParams: 规则参数
// 返回值:
// - 语句接口,当错误为nil时,语句接口也可能是nil,代表该规则并没有被赋值,所以是没有条件的
// - 错误
func FormConditionClauseByRule(domainName string, r Rule, ruleParams map[string]any) (clause.Clause, error) {
	c, err := formConditionClause(domainName, r, ruleParams)
	if err != nil {
		return nil, err
	}

	return c, nil
}

const (
	modifyBranchRoot  = "root"
	modifyBranchLeft  = "left"
	modifyBranchRight = "right"
)

func ModifyLeafRuleStr(ruleStr string, leafRuleCallback func(leafRule *Rule) error) (string, error) {
	rootRule := new(Rule)
	err := json.Unmarshal([]byte(ruleStr), rootRule)
	if err != nil {
		return "", err
	}

	err = modifyLeafRule(nil, rootRule, modifyBranchRoot, leafRuleCallback)
	if err != nil {
		return "", err
	}

	newRuleStr, err := json.Marshal(rootRule)
	if err != nil {
		return "", err
	}

	return string(newRuleStr), nil
}

func modifyLeafRule(parentRule *Rule, r *Rule, branch string, leafRuleCallback func(leafRule *Rule) error) error {
	if r == nil {
		return nil
	}

	err := r.check()
	if err != nil {
		return err
	}

	if !r.isNode() {
		err := leafRuleCallback(r)
		if err != nil {
			return err
		}

		if parentRule == nil {
			return nil
		}

		switch branch {
		case modifyBranchRoot:
			return nil
		case modifyBranchLeft:
			parentRule.Left = r
		case modifyBranchRight:
			parentRule.Right = r
		default:
			return errors.New("不支持的分支类型: " + branch)
		}
	}

	err = modifyLeafRule(r, r.Left, modifyBranchLeft, leafRuleCallback)
	if err != nil {
		return err
	}

	err = modifyLeafRule(r, r.Right, modifyBranchRight, leafRuleCallback)
	if err != nil {
		return err
	}

	return nil
}

func modifyRule(rootRule *Rule, ruleCallback func(parentRule *Rule, r *Rule, branch string) error) error {
	if rootRule == nil {
		return nil
	}

	err := rootRule.check()
	if err != nil {
		return err
	}

	if !rootRule.isNode() {
		return ruleCallback(nil, rootRule, modifyBranchRoot)
	}

	err = ruleCallback(rootRule, rootRule.Left, modifyBranchLeft)
	if err != nil {
		return err
	}

	if rootRule.Left.isNode() {
		err := modifyRule(rootRule.Left, ruleCallback)
		if err != nil {
			return err
		}
	}

	err = ruleCallback(rootRule, rootRule.Right, modifyBranchRight)
	if err != nil {
		return err
	}

	if rootRule.Right.isNode() {
		err := modifyRule(rootRule.Right, ruleCallback)
		if err != nil {
			return err
		}
	}

	return nil
}

func getEnabledRule(dbSchema string, scope string, domainName string, i *infrastructure.Infrastructure) (Rule, error) {
	dbExecutor := i.DBExecutor()

	result, err := database.QueryOne(dbExecutor, &sql.QueryOneExecuteParams{
		TableName: domain.TableName(dbSchema, &Entity{}),
		Conditions: sql.NewConditions().
			Equal(ColumnScope, scope).
			Equal(ColumnDomainName, domainName).
			Equal(ColumnEnabled, true),
	})
	if err != nil {
		return Rule{}, err
	}

	r := new(Rule)
	err = json.Unmarshal([]byte(result.ColumnValueString(ColumnRule)), r)
	if err != nil {
		return Rule{}, err
	}

	return *r, nil
}

func formConditionClause(domainName string, r Rule, ruleParams map[string]any) (clause.Clause, error) {
	err := r.check()
	if err != nil {
		return nil, err
	}

	if strutils.IsStringEmpty(r.LogicalOperator) {
		columnName, err := definition.GetColumnName(domainName, r.FieldName)
		if err != nil {
			return nil, err
		}

		ruleValue := r.Value

		if ruleParams != nil {
			ruleParamValue, ok := ruleParams[columnName]
			if ok {
				ruleValue = ruleParamValue
			}
		}

		if ruleValue == nil {
			return nil, nil
		}

		conditions := clause.NewConditions()

		err = definition.AddCondition(conditions, r.Operator, columnName, ruleValue)
		if err != nil {
			return nil, err
		}

		return conditions.And(), nil
	}

	leftClause, err := formConditionClause(domainName, *r.Left, ruleParams)
	if err != nil {
		return nil, err
	}

	rightClause, err := formConditionClause(domainName, *r.Right, ruleParams)
	if err != nil {
		return nil, err
	}

	switch r.LogicalOperator {
	case LogicalOperatorAnd:
		return clause.NewConditionJoin(leftClause, rightClause).And(), nil
	case LogicalOperatorOr:
		return clause.NewConditionJoin(leftClause, rightClause).Or(), nil
	default:
		return clause.NewConditionJoin(leftClause, rightClause).And(), nil
	}
}