// SiYuan - Refactor your thinking
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

package av

import (
	"strings"
	"time"

	"github.com/88250/lute/ast"
	"github.com/siyuan-note/siyuan/kernel/util"
)

type Filterable interface {
	FilterRows(attrView *AttributeView)
}

type ViewFilter struct {
	Column        string         `json:"column"`
	Operator      FilterOperator `json:"operator"`
	Value         *Value         `json:"value"`
	RelativeDate  *RelativeDate  `json:"relativeDate"`
	RelativeDate2 *RelativeDate  `json:"relativeDate2"`
}

type RelativeDateUnit int

const (
	RelativeDateUnitDay = iota
	RelativeDateUnitWeek
	RelativeDateUnitMonth
	RelativeDateUnitYear
)

type RelativeDateDirection int

const (
	RelativeDateDirectionBefore = -1
	RelativeDateDirectionThis   = 0
	RelativeDateDirectionAfter  = 1
)

type RelativeDate struct {
	Count     int                   `json:"count"`     // 数量
	Unit      RelativeDateUnit      `json:"unit"`      // 单位：0 天、1 周、2 月、3 年
	Direction RelativeDateDirection `json:"direction"` // 方向：-1 前、0 当前、1 后
}

type FilterOperator string

const (
	FilterOperatorIsEqual          FilterOperator = "="
	FilterOperatorIsNotEqual       FilterOperator = "!="
	FilterOperatorIsGreater        FilterOperator = ">"
	FilterOperatorIsGreaterOrEqual FilterOperator = ">="
	FilterOperatorIsLess           FilterOperator = "<"
	FilterOperatorIsLessOrEqual    FilterOperator = "<="
	FilterOperatorContains         FilterOperator = "Contains"
	FilterOperatorDoesNotContain   FilterOperator = "Does not contains"
	FilterOperatorIsEmpty          FilterOperator = "Is empty"
	FilterOperatorIsNotEmpty       FilterOperator = "Is not empty"
	FilterOperatorStartsWith       FilterOperator = "Starts with"
	FilterOperatorEndsWith         FilterOperator = "Ends with"
	FilterOperatorIsBetween        FilterOperator = "Is between"
	FilterOperatorIsTrue           FilterOperator = "Is true"
	FilterOperatorIsFalse          FilterOperator = "Is false"
)

func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, rowID string, attrViewCache *map[string]*AttributeView) bool {
	if nil == filter || (nil == filter.Value && nil == filter.RelativeDate) {
		return true
	}

	if nil != filter.Value && value.Type != filter.Value.Type {
		// 由于字段类型被用户编辑过导致和过滤器值类型不匹配，该情况下不过滤
		return true
	}

	if nil != value.Rollup && KeyTypeRollup == value.Type && nil != filter.Value && KeyTypeRollup == filter.Value.Type &&
		nil != filter.Value.Rollup && 0 < len(filter.Value.Rollup.Contents) {
		// 单独处理汇总类型的比较

		// 处理为空和不为空
		switch filter.Operator {
		case FilterOperatorIsEmpty:
			return 0 == len(value.Rollup.Contents)
		case FilterOperatorIsNotEmpty:
			return 0 != len(value.Rollup.Contents)
		}

		// 处理值比较
		key, _ := attrView.GetKey(value.KeyID)
		if nil == key {
			return false
		}

		relKey, _ := attrView.GetKey(key.Rollup.RelationKeyID)
		if nil == relKey {
			return false
		}

		relVal := attrView.GetValue(relKey.ID, rowID)
		if nil == relVal || nil == relVal.Relation {
			return false
		}

		destAv := (*attrViewCache)[relKey.Relation.AvID]
		if nil == destAv {
			destAv, _ = ParseAttributeView(relKey.Relation.AvID)
			if nil != destAv {
				(*attrViewCache)[relKey.Relation.AvID] = destAv
			}
		}
		if nil == destAv {
			return false
		}

		destKey, _ := destAv.GetKey(key.Rollup.KeyID)
		if nil == destKey {
			return false
		}

		for _, blockID := range relVal.Relation.BlockIDs {
			destVal := destAv.GetValue(key.Rollup.KeyID, blockID)
			if nil == destVal {
				if destAv.ExistBlock(blockID) { // 数据库中存在行但是列值不存在是数据未初始化，这里补一个默认值
					destVal = GetAttributeViewDefaultValue(ast.NewNodeID(), key.Rollup.KeyID, blockID, destKey.Type)
				}
				if nil == destVal {
					continue
				}
			}

			switch filter.Operator {
			case FilterOperatorContains:
				if destVal.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
					return true
				}
			case FilterOperatorDoesNotContain:
				ret := destVal.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator)
				if !ret {
					return false
				}
			default:
				if destVal.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
					return true
				}
			}
		}

		switch filter.Operator {
		case FilterOperatorContains:
			return false
		case FilterOperatorDoesNotContain:
			return true
		default:
			return false
		}
	}

	if nil != value.Relation && KeyTypeRelation == value.Type && nil != filter.Value && KeyTypeRelation == filter.Value.Type &&
		nil != filter.Value.Relation && 0 < len(filter.Value.Relation.BlockIDs) {
		// 单独处理关联类型的比较

		// 处理为空和不为空
		switch filter.Operator {
		case FilterOperatorIsEmpty:
			return 0 == len(value.Relation.Contents)
		case FilterOperatorIsNotEmpty:
			return 0 != len(value.Relation.Contents)
		}

		for _, relationValue := range value.Relation.Contents {
			filterValue := &Value{Type: KeyTypeBlock, Block: &ValueBlock{Content: filter.Value.Relation.BlockIDs[0]}}

			switch filter.Operator {
			case FilterOperatorContains:
				if relationValue.filter(filterValue, filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
					return true
				}
			case FilterOperatorDoesNotContain:
				ret := relationValue.filter(filterValue, filter.RelativeDate, filter.RelativeDate2, filter.Operator)
				if !ret {
					return false
				}
			default:
				if relationValue.filter(filterValue, filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
					return true
				}
			}
		}

		switch filter.Operator {
		case FilterOperatorContains:
			return false
		case FilterOperatorDoesNotContain:
			return true
		default:
			return false
		}
	}
	return value.filter(filter.Value, filter.RelativeDate, filter.RelativeDate2, filter.Operator)
}

func (value *Value) filter(other *Value, relativeDate, relativeDate2 *RelativeDate, operator FilterOperator) bool {
	switch value.Type {
	case KeyTypeBlock:
		if nil != value.Block && nil != other && nil != other.Block {
			switch operator {
			case FilterOperatorIsEqual:
				return value.Block.Content == other.Block.Content
			case FilterOperatorIsNotEqual:
				return value.Block.Content != other.Block.Content
			case FilterOperatorContains:
				return strings.Contains(value.Block.Content, other.Block.Content)
			case FilterOperatorDoesNotContain:
				return !strings.Contains(value.Block.Content, other.Block.Content)
			case FilterOperatorStartsWith:
				return strings.HasPrefix(value.Block.Content, other.Block.Content)
			case FilterOperatorEndsWith:
				return strings.HasSuffix(value.Block.Content, other.Block.Content)
			case FilterOperatorIsEmpty:
				return "" == strings.TrimSpace(value.Block.Content)
			case FilterOperatorIsNotEmpty:
				return "" != strings.TrimSpace(value.Block.Content)
			}
		}
	case KeyTypeText:
		if nil != value.Text && nil != other && nil != other.Text {
			switch operator {
			case FilterOperatorIsEqual:
				if "" == strings.TrimSpace(other.Text.Content) {
					return true
				}
				return value.Text.Content == other.Text.Content
			case FilterOperatorIsNotEqual:
				if "" == strings.TrimSpace(other.Text.Content) {
					return true
				}
				return value.Text.Content != other.Text.Content
			case FilterOperatorContains:
				if "" == strings.TrimSpace(other.Text.Content) {
					return true
				}
				return strings.Contains(value.Text.Content, other.Text.Content)
			case FilterOperatorDoesNotContain:
				if "" == strings.TrimSpace(other.Text.Content) {
					return true
				}
				return !strings.Contains(value.Text.Content, other.Text.Content)
			case FilterOperatorStartsWith:
				if "" == strings.TrimSpace(other.Text.Content) {
					return true
				}
				return strings.HasPrefix(value.Text.Content, other.Text.Content)
			case FilterOperatorEndsWith:
				if "" == strings.TrimSpace(other.Text.Content) {
					return true
				}
				return strings.HasSuffix(value.Text.Content, other.Text.Content)
			case FilterOperatorIsEmpty:
				return "" == strings.TrimSpace(value.Text.Content)
			case FilterOperatorIsNotEmpty:
				return "" != strings.TrimSpace(value.Text.Content)
			}
		}
	case KeyTypeNumber:
		if nil != value.Number && nil != other && nil != other.Number {
			switch operator {
			case FilterOperatorIsEqual:
				if !other.Number.IsNotEmpty {
					return true
				}
				return value.Number.Content == other.Number.Content
			case FilterOperatorIsNotEqual:
				if !other.Number.IsNotEmpty {
					return true
				}
				return value.Number.Content != other.Number.Content
			case FilterOperatorIsGreater:
				return value.Number.Content > other.Number.Content
			case FilterOperatorIsGreaterOrEqual:
				return value.Number.Content >= other.Number.Content
			case FilterOperatorIsLess:
				return value.Number.Content < other.Number.Content
			case FilterOperatorIsLessOrEqual:
				return value.Number.Content <= other.Number.Content
			case FilterOperatorIsEmpty:
				return !value.Number.IsNotEmpty
			case FilterOperatorIsNotEmpty:
				return value.Number.IsNotEmpty
			}
		}
	case KeyTypeDate:
		if nil != value.Date {
			switch operator {
			case FilterOperatorIsEmpty:
				return !value.Date.IsNotEmpty
			case FilterOperatorIsNotEmpty:
				return value.Date.IsNotEmpty
			}

			if !value.Date.IsNotEmpty {
				// 空值不进行比较，直接排除
				// Database date filter excludes empty values https://github.com/siyuan-note/siyuan/issues/11061
				return false
			}

			if nil != relativeDate { // 使用相对时间比较
				relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(relativeDate.Count, relativeDate.Unit, relativeDate.Direction)
				relativeTimeStart2, relativeTimeEnd2 := calcRelativeTimeRegion(relativeDate2.Count, relativeDate2.Unit, relativeDate2.Direction)
				return filterRelativeTime(value.Date.Content, value.Date.IsNotEmpty, relativeTimeStart, relativeTimeEnd, relativeTimeStart2, relativeTimeEnd2, operator)
			} else { // 使用具体时间比较
				if nil == other.Date {
					return true
				}
				return filterTime(value.Date.Content, value.Date.IsNotEmpty, other.Date.Content, other.Date.Content2, operator)
			}
		}
	case KeyTypeCreated:
		if nil != value.Created {
			if nil != relativeDate { // 使用相对时间比较
				relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(relativeDate.Count, relativeDate.Unit, relativeDate.Direction)
				relativeTimeStart2, relativeTimeEnd2 := calcRelativeTimeRegion(relativeDate2.Count, relativeDate2.Unit, relativeDate2.Direction)
				return filterRelativeTime(value.Created.Content, true, relativeTimeStart, relativeTimeEnd, relativeTimeStart2, relativeTimeEnd2, operator)
			} else { // 使用具体时间比较
				if nil == other.Created {
					return true
				}
				return filterTime(value.Created.Content, value.Created.IsNotEmpty, other.Created.Content, other.Created.Content2, operator)
			}
		}
	case KeyTypeUpdated:
		if nil != value.Updated {
			if nil != relativeDate { // 使用相对时间比较
				relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(relativeDate.Count, relativeDate.Unit, relativeDate.Direction)
				relativeTimeStart2, relativeTimeEnd2 := calcRelativeTimeRegion(relativeDate2.Count, relativeDate2.Unit, relativeDate2.Direction)
				return filterRelativeTime(value.Updated.Content, true, relativeTimeStart, relativeTimeEnd, relativeTimeStart2, relativeTimeEnd2, operator)
			} else { // 使用具体时间比较
				if nil == other.Updated {
					return true
				}

				return filterTime(value.Updated.Content, value.Updated.IsNotEmpty, other.Updated.Content, other.Updated.Content2, operator)
			}
		}
	case KeyTypeSelect, KeyTypeMSelect:
		if nil != value.MSelect {
			if nil != other && nil != other.MSelect {
				switch operator {
				case FilterOperatorIsEqual, FilterOperatorContains:
					contains := false
					for _, v := range value.MSelect {
						for _, v2 := range other.MSelect {
							if v.Content == v2.Content {
								contains = true
								break
							}
						}
					}
					return contains
				case FilterOperatorIsNotEqual, FilterOperatorDoesNotContain:
					contains := false
					for _, v := range value.MSelect {
						for _, v2 := range other.MSelect {
							if v.Content == v2.Content {
								contains = true
								break
							}
						}
					}
					return !contains
				case FilterOperatorIsEmpty:
					return 0 == len(value.MSelect) || 1 == len(value.MSelect) && "" == value.MSelect[0].Content
				case FilterOperatorIsNotEmpty:
					return 0 != len(value.MSelect) && !(1 == len(value.MSelect) && "" == value.MSelect[0].Content)
				}
				return false
			}

			// 没有设置比较值

			switch operator {
			case FilterOperatorIsEqual, FilterOperatorIsNotEqual, FilterOperatorContains, FilterOperatorDoesNotContain:
				return true
			case FilterOperatorIsEmpty:
				return 0 == len(value.MSelect) || 1 == len(value.MSelect) && "" == value.MSelect[0].Content
			case FilterOperatorIsNotEmpty:
				return 0 != len(value.MSelect) && !(1 == len(value.MSelect) && "" == value.MSelect[0].Content)
			}
		}
	case KeyTypeURL:
		if nil != value.URL && nil != other && nil != other.URL {
			switch operator {
			case FilterOperatorIsEqual:
				return value.URL.Content == other.URL.Content
			case FilterOperatorIsNotEqual:
				return value.URL.Content != other.URL.Content
			case FilterOperatorContains:
				return strings.Contains(value.URL.Content, other.URL.Content)
			case FilterOperatorDoesNotContain:
				return !strings.Contains(value.URL.Content, other.URL.Content)
			case FilterOperatorStartsWith:
				return strings.HasPrefix(value.URL.Content, other.URL.Content)
			case FilterOperatorEndsWith:
				return strings.HasSuffix(value.URL.Content, other.URL.Content)
			case FilterOperatorIsEmpty:
				return "" == strings.TrimSpace(value.URL.Content)
			case FilterOperatorIsNotEmpty:
				return "" != strings.TrimSpace(value.URL.Content)
			}
		}
	case KeyTypeEmail:
		if nil != value.Email && nil != other && nil != other.Email {
			switch operator {
			case FilterOperatorIsEqual:
				return value.Email.Content == other.Email.Content
			case FilterOperatorIsNotEqual:
				return value.Email.Content != other.Email.Content
			case FilterOperatorContains:
				return strings.Contains(value.Email.Content, other.Email.Content)
			case FilterOperatorDoesNotContain:
				return !strings.Contains(value.Email.Content, other.Email.Content)
			case FilterOperatorStartsWith:
				return strings.HasPrefix(value.Email.Content, other.Email.Content)
			case FilterOperatorEndsWith:
				return strings.HasSuffix(value.Email.Content, other.Email.Content)
			case FilterOperatorIsEmpty:
				return "" == strings.TrimSpace(value.Email.Content)
			case FilterOperatorIsNotEmpty:
				return "" != strings.TrimSpace(value.Email.Content)
			}
		}
	case KeyTypePhone:
		if nil != value.Phone && nil != other && nil != other.Phone {
			switch operator {
			case FilterOperatorIsEqual:
				return value.Phone.Content == other.Phone.Content
			case FilterOperatorIsNotEqual:
				return value.Phone.Content != other.Phone.Content
			case FilterOperatorContains:
				return strings.Contains(value.Phone.Content, other.Phone.Content)
			case FilterOperatorDoesNotContain:
				return !strings.Contains(value.Phone.Content, other.Phone.Content)
			case FilterOperatorStartsWith:
				return strings.HasPrefix(value.Phone.Content, other.Phone.Content)
			case FilterOperatorEndsWith:
				return strings.HasSuffix(value.Phone.Content, other.Phone.Content)
			case FilterOperatorIsEmpty:
				return "" == strings.TrimSpace(value.Phone.Content)
			case FilterOperatorIsNotEmpty:
				return "" != strings.TrimSpace(value.Phone.Content)
			}
		}
	case KeyTypeMAsset:
		if nil != value.MAsset && nil != other && nil != other.MAsset && 0 < len(value.MAsset) && 0 < len(other.MAsset) {
			switch operator {
			case FilterOperatorIsEqual, FilterOperatorContains:
				contains := false
				for _, v := range value.MAsset {
					for _, v2 := range other.MAsset {
						if v.Content == v2.Content {
							contains = true
							break
						}
					}
				}
				return contains
			case FilterOperatorIsNotEqual, FilterOperatorDoesNotContain:
				contains := false
				for _, v := range value.MAsset {
					for _, v2 := range other.MAsset {
						if v.Content == v2.Content {
							contains = true
							break
						}
					}
				}
				return !contains
			case FilterOperatorIsEmpty:
				return 0 == len(value.MAsset) || 1 == len(value.MAsset) && "" == value.MAsset[0].Content
			case FilterOperatorIsNotEmpty:
				return 0 != len(value.MAsset) && !(1 == len(value.MAsset) && "" == value.MAsset[0].Content)
			}
		}
	case KeyTypeTemplate:
		if nil != value.Template && nil != other && nil != other.Template {
			switch operator {
			case FilterOperatorIsEqual:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return value.Template.Content == other.Template.Content
			case FilterOperatorIsNotEqual:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return value.Template.Content != other.Template.Content
			case FilterOperatorIsGreater:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return value.Template.Content > other.Template.Content
			case FilterOperatorIsGreaterOrEqual:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return value.Template.Content >= other.Template.Content
			case FilterOperatorIsLess:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return value.Template.Content < other.Template.Content
			case FilterOperatorIsLessOrEqual:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return value.Template.Content <= other.Template.Content
			case FilterOperatorContains:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return strings.Contains(value.Template.Content, other.Template.Content)
			case FilterOperatorDoesNotContain:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return !strings.Contains(value.Template.Content, other.Template.Content)
			case FilterOperatorStartsWith:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return strings.HasPrefix(value.Template.Content, other.Template.Content)
			case FilterOperatorEndsWith:
				if "" == strings.TrimSpace(other.Template.Content) {
					return true
				}
				return strings.HasSuffix(value.Template.Content, other.Template.Content)
			case FilterOperatorIsEmpty:
				return "" == strings.TrimSpace(value.Template.Content)
			case FilterOperatorIsNotEmpty:
				return "" != strings.TrimSpace(value.Template.Content)
			}
		}
	case KeyTypeCheckbox:
		if nil != value.Checkbox {
			switch operator {
			case FilterOperatorIsTrue:
				return value.Checkbox.Checked
			case FilterOperatorIsFalse:
				return !value.Checkbox.Checked
			}
		}
	}

	switch operator {
	case FilterOperatorIsEmpty:
		return value.IsEmpty()
	case FilterOperatorIsNotEmpty:
		return !value.IsEmpty()
	}
	return false
}

func filterRelativeTime(valueMills int64, valueIsNotEmpty bool, otherValueStart, otherValueEnd, otherValueStart2, otherValueEnd2 time.Time, operator FilterOperator) bool {
	valueTime := time.UnixMilli(valueMills)
	switch operator {
	case FilterOperatorIsEqual:
		return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && valueTime.Before(otherValueEnd)
	case FilterOperatorIsNotEqual:
		return valueTime.Before(otherValueStart) || valueTime.After(otherValueEnd)
	case FilterOperatorIsGreater:
		return valueTime.After(otherValueEnd) || valueTime.Equal(otherValueEnd)
	case FilterOperatorIsGreaterOrEqual:
		return valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)
	case FilterOperatorIsLess:
		return valueTime.Before(otherValueStart)
	case FilterOperatorIsLessOrEqual:
		return valueTime.Before(otherValueEnd) || valueTime.Equal(otherValueEnd)
	case FilterOperatorIsBetween:
		return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && valueTime.Before(otherValueStart2)
	case FilterOperatorIsEmpty:
		return !valueIsNotEmpty
	case FilterOperatorIsNotEmpty:
		return valueIsNotEmpty
	}
	return false
}

func filterTime(valueMills int64, valueIsNotEmpty bool, otherValueMills, otherValueMills2 int64, operator FilterOperator) bool {
	valueTime := time.UnixMilli(valueMills)
	otherValueTime := time.UnixMilli(otherValueMills)
	otherValueStart := time.Date(otherValueTime.Year(), otherValueTime.Month(), otherValueTime.Day(), 0, 0, 0, 0, otherValueTime.Location())
	otherValueEnd := time.Date(otherValueTime.Year(), otherValueTime.Month(), otherValueTime.Day(), 23, 59, 59, 999999999, otherValueTime.Location())
	switch operator {
	case FilterOperatorIsEqual:
		return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && valueTime.Before(otherValueEnd)
	case FilterOperatorIsNotEqual:
		return valueTime.Before(otherValueStart) || valueTime.After(otherValueEnd)
	case FilterOperatorIsGreater:
		return valueTime.After(otherValueEnd) || valueTime.Equal(otherValueEnd)
	case FilterOperatorIsGreaterOrEqual:
		return valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)
	case FilterOperatorIsLess:
		return valueTime.Before(otherValueStart)
	case FilterOperatorIsLessOrEqual:
		return valueTime.Before(otherValueEnd) || valueTime.Equal(otherValueEnd)
	case FilterOperatorIsBetween:
		otherValueTime2 := time.UnixMilli(otherValueMills2)
		otherValueEnd2 := time.Date(otherValueTime2.Year(), otherValueTime2.Month(), otherValueTime2.Day(), 23, 59, 59, 999999999, otherValueTime2.Location())
		return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && (valueTime.Before(otherValueEnd2) || valueTime.Equal(otherValueEnd2))
	case FilterOperatorIsEmpty:
		return !valueIsNotEmpty
	case FilterOperatorIsNotEmpty:
		return valueIsNotEmpty
	}
	return false
}

// 根据 Count、Unit 和 Direction 计算相对当前时间的开始时间和结束时间
func calcRelativeTimeRegion(count int, unit RelativeDateUnit, direction RelativeDateDirection) (start, end time.Time) {
	now := time.Now()
	switch unit {
	case RelativeDateUnitDay:
		switch direction {
		case RelativeDateDirectionBefore:
			// 结束时间：今天的 0 点
			end = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
			// 开始时间：结束时间减去 count 天
			start = end.AddDate(0, 0, -count)
		case RelativeDateDirectionThis:
			// 开始时间：今天的 0 点
			start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
			// 结束时间：今天的 23:59:59.999999999
			end = time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 999999999, now.Location())
		case RelativeDateDirectionAfter:
			// 开始时间：今天的 23:59:59.999999999
			start = time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 999999999, now.Location())
			// 结束时间：开始时间加上 count 天
			end = start.AddDate(0, 0, count)
		}
	case RelativeDateUnitWeek:
		weekday := int(now.Weekday())
		if 0 == weekday {
			weekday = 7
		}
		switch direction {
		case RelativeDateDirectionBefore:
			// 结束时间：本周的周一
			end = time.Date(now.Year(), now.Month(), now.Day()-weekday+1, 0, 0, 0, 0, now.Location())
			// 开始时间：结束时间减去 count*7 天
			start = end.AddDate(0, 0, -count*7)
		case RelativeDateDirectionThis:
			// 开始时间：本周的周一
			start = time.Date(now.Year(), now.Month(), now.Day()-weekday+1, 0, 0, 0, 0, now.Location())
			// 结束时间：本周的周日
			end = time.Date(now.Year(), now.Month(), now.Day()-weekday+7, 23, 59, 59, 999999999, now.Location())
		case RelativeDateDirectionAfter:
			//  开始时间：本周的周日
			start = time.Date(now.Year(), now.Month(), now.Day()-weekday+7, 23, 59, 59, 999999999, now.Location())
			// 结束时间：开始时间加上 count*7 天
			end = start.AddDate(0, 0, count*7)
		}
	case RelativeDateUnitMonth:
		switch direction {
		case RelativeDateDirectionBefore:
			// 结束时间：本月的 1 号
			end = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
			// 开始时间：结束时间减去 count 个月
			start = end.AddDate(0, -count, 0)
		case RelativeDateDirectionThis:
			// 开始时间：本月的 1 号
			start = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
			// 结束时间：下个月的 1 号减去 1 纳秒
			end = time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Nanosecond)
		case RelativeDateDirectionAfter:
			// 开始时间：下个月的 1 号减去 1 纳秒
			start = time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Nanosecond)
			// 结束时间：开始时间加上 count 个月
			end = start.AddDate(0, count, 0)
		}
	case RelativeDateUnitYear:
		switch direction {
		case RelativeDateDirectionBefore:
			// 结束时间：今年的 1 月 1 号
			end = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())
			// 开始时间：结束时间减去 count 年
			start = end.AddDate(-count, 0, 0)
		case RelativeDateDirectionThis:
			// 开始时间：今年的 1 月 1 号
			start = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())
			// 结束时间：明年的 1 月 1 号减去 1 纳秒
			end = time.Date(now.Year()+1, 1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Nanosecond)
		case RelativeDateDirectionAfter:
			// 开始时间：今年的 12 月 31 号
			start = time.Date(now.Year(), 12, 31, 23, 59, 59, 999999999, now.Location())
			// 结束时间：开始时间加上 count 年
			end = start.AddDate(count, 0, 0)
		}
	}
	return
}

func (filter *ViewFilter) GetAffectValue(key *Key, defaultVal *Value) (ret *Value) {
	if nil != filter.Value {
		if KeyTypeRelation == filter.Value.Type || KeyTypeTemplate == filter.Value.Type || KeyTypeRollup == filter.Value.Type || KeyTypeUpdated == filter.Value.Type || KeyTypeCreated == filter.Value.Type {
			// 所有生成的数据都不设置默认值
			return nil
		}
	}

	if nil == filter.Value {
		if nil != filter.RelativeDate {
			// 相对日期今天的动态日期不设置默认值
			return nil
		}
		// 两个值都空的情况下也不设置默认值
		return nil
	}

	if FilterOperatorIsEmpty != filter.Operator && FilterOperatorIsNotEmpty != filter.Operator {
		if filter.Value.IsEmpty() {
			// 在不是过滤空值和非空值的情况下，空值不设置默认值 https://github.com/siyuan-note/siyuan/issues/11297
			return nil
		}
	}

	ret = filter.Value.Clone()
	ret.CreatedAt = util.CurrentTimeMillis()
	ret.UpdatedAt = ret.CreatedAt + 1000

	if nil != defaultVal {
		// 如果有默认值则优先使用默认值
		clonedDefaultVal := defaultVal.Clone()
		defaultRawVal := clonedDefaultVal.GetValByType(filter.Value.Type)
		if nil != defaultRawVal {
			ret.SetValByType(filter.Value.Type, defaultRawVal)
			return
		}
	}
	// 没有默认值则使用过滤条件的值

	switch filter.Value.Type {
	case KeyTypeBlock:
		switch filter.Operator {
		case FilterOperatorIsEqual:
			ret.Block = &ValueBlock{ID: filter.Value.Block.ID, Content: filter.Value.Block.Content}
		case FilterOperatorIsNotEqual:
			ret.Block = &ValueBlock{ID: filter.Value.Block.ID, Content: ""}
		case FilterOperatorContains:
			ret.Block = &ValueBlock{ID: filter.Value.Block.ID, Content: filter.Value.Block.Content}
		case FilterOperatorDoesNotContain:
			ret.Block = &ValueBlock{ID: filter.Value.Block.ID, Content: ""}
		case FilterOperatorStartsWith:
			ret.Block = &ValueBlock{ID: filter.Value.Block.ID, Content: filter.Value.Block.Content}
		case FilterOperatorEndsWith:
			ret.Block = &ValueBlock{ID: filter.Value.Block.ID, Content: filter.Value.Block.Content}
		case FilterOperatorIsEmpty:
			ret.Block = &ValueBlock{ID: filter.Value.Block.ID, Content: ""}
		case FilterOperatorIsNotEmpty:
			ret.Block = &ValueBlock{ID: filter.Value.Block.ID, Content: ""}
		}
	case KeyTypeText:
		switch filter.Operator {
		case FilterOperatorIsEqual:
			ret.Text = &ValueText{Content: filter.Value.Text.Content}
		case FilterOperatorIsNotEqual:
			ret.Text = &ValueText{Content: ""}
		case FilterOperatorContains:
			ret.Text = &ValueText{Content: filter.Value.Text.Content}
		case FilterOperatorDoesNotContain:
			ret.Text = &ValueText{Content: ""}
		case FilterOperatorStartsWith:
			ret.Text = &ValueText{Content: filter.Value.Text.Content}
		case FilterOperatorEndsWith:
			ret.Text = &ValueText{Content: filter.Value.Text.Content}
		case FilterOperatorIsEmpty:
			ret.Text = &ValueText{Content: ""}
		case FilterOperatorIsNotEmpty:
			ret.Text = &ValueText{Content: ""}
		}
	case KeyTypeNumber:
		switch filter.Operator {
		case FilterOperatorIsEqual:
			ret.Number = &ValueNumber{Content: filter.Value.Number.Content, IsNotEmpty: false}
		case FilterOperatorIsNotEqual:
			if 0 == filter.Value.Number.Content {
				ret.Number = &ValueNumber{Content: 1, IsNotEmpty: true}
			} else {
				ret.Number = &ValueNumber{Content: 0, IsNotEmpty: true}
			}
		case FilterOperatorIsGreater:
			ret.Number = &ValueNumber{Content: filter.Value.Number.Content + 1, IsNotEmpty: true}
		case FilterOperatorIsGreaterOrEqual:
			ret.Number = &ValueNumber{Content: filter.Value.Number.Content, IsNotEmpty: true}
		case FilterOperatorIsLess:
			ret.Number = &ValueNumber{Content: filter.Value.Number.Content - 1, IsNotEmpty: true}
		case FilterOperatorIsLessOrEqual:
			ret.Number = &ValueNumber{Content: filter.Value.Number.Content, IsNotEmpty: true}
		case FilterOperatorIsEmpty:
			ret.Number = &ValueNumber{Content: 0, IsNotEmpty: false}
		case FilterOperatorIsNotEmpty:
			ret.Number = &ValueNumber{Content: 0, IsNotEmpty: true}
		}
	case KeyTypeDate:
		switch filter.Operator {
		case FilterOperatorIsEqual:
			ret.Date = &ValueDate{Content: filter.Value.Date.Content, IsNotEmpty: true}
		case FilterOperatorIsNotEqual:
			ret.Date = &ValueDate{Content: util.CurrentTimeMillis(), IsNotEmpty: true}
		case FilterOperatorIsGreater:
			ret.Date = &ValueDate{Content: filter.Value.Date.Content + 1000*60, IsNotEmpty: true}
		case FilterOperatorIsGreaterOrEqual:
			ret.Date = &ValueDate{Content: filter.Value.Date.Content, IsNotEmpty: true}
		case FilterOperatorIsLess:
			ret.Date = &ValueDate{Content: filter.Value.Date.Content - 1000*60, IsNotEmpty: true}
		case FilterOperatorIsLessOrEqual:
			ret.Date = &ValueDate{Content: filter.Value.Date.Content, IsNotEmpty: true}
		case FilterOperatorIsBetween:
			ret.Date = &ValueDate{Content: filter.Value.Date.Content - 1000*60, IsNotEmpty: true}
		case FilterOperatorIsEmpty:
			ret.Date = &ValueDate{Content: 0, IsNotEmpty: false}
		case FilterOperatorIsNotEmpty:
			ret.Date = &ValueDate{Content: util.CurrentTimeMillis(), IsNotEmpty: true}
		}
	case KeyTypeSelect, KeyTypeMSelect:
		switch filter.Operator {
		case FilterOperatorIsEqual:
			valueSelect := &ValueSelect{Content: "", Color: "1"}
			if 0 < len(key.Options) {
				valueSelect.Color = key.Options[0].Color
			}
			if 0 < len(filter.Value.MSelect) {
				valueSelect.Content = filter.Value.MSelect[0].Content
				valueSelect.Color = filter.Value.MSelect[0].Color
			}
			ret.MSelect = []*ValueSelect{valueSelect}
		case FilterOperatorIsNotEqual:
			if 0 < len(filter.Value.MSelect) {
				ret.MSelect = []*ValueSelect{}
			}
		case FilterOperatorIsEmpty:
			ret.MSelect = []*ValueSelect{}
		case FilterOperatorIsNotEmpty:
			if 0 < len(key.Options) {
				ret.MSelect = []*ValueSelect{{Content: key.Options[0].Name, Color: key.Options[0].Color}}
			}
		}
	case KeyTypeURL:
		switch filter.Operator {
		case FilterOperatorIsEqual:
			ret.URL = &ValueURL{Content: filter.Value.URL.Content}
		case FilterOperatorIsNotEqual:
			ret.URL = &ValueURL{Content: filter.Value.URL.Content}
		case FilterOperatorContains:
			ret.URL = &ValueURL{Content: filter.Value.URL.Content}
		case FilterOperatorDoesNotContain:
			ret.URL = &ValueURL{Content: ""}
		case FilterOperatorStartsWith:
			ret.URL = &ValueURL{Content: filter.Value.URL.Content}
		case FilterOperatorEndsWith:
			ret.URL = &ValueURL{Content: filter.Value.URL.Content}
		case FilterOperatorIsEmpty:
			ret.URL = &ValueURL{}
		case FilterOperatorIsNotEmpty:
			ret.URL = &ValueURL{}
		}
	case KeyTypeEmail:
		switch filter.Operator {
		case FilterOperatorIsEqual:
			ret.Email = &ValueEmail{Content: filter.Value.Email.Content}
		case FilterOperatorIsNotEqual:
			ret.Email = &ValueEmail{Content: filter.Value.Email.Content}
		case FilterOperatorContains:
			ret.Email = &ValueEmail{Content: filter.Value.Email.Content}
		case FilterOperatorDoesNotContain:
			ret.Email = &ValueEmail{Content: ""}
		case FilterOperatorStartsWith:
			ret.Email = &ValueEmail{Content: filter.Value.Email.Content}
		case FilterOperatorEndsWith:
			ret.Email = &ValueEmail{Content: filter.Value.Email.Content}
		case FilterOperatorIsEmpty:
			ret.Email = &ValueEmail{Content: ""}
		case FilterOperatorIsNotEmpty:
			ret.Email = &ValueEmail{Content: ""}
		}
	case KeyTypePhone:
		switch filter.Operator {
		case FilterOperatorIsEqual:
			ret.Phone = &ValuePhone{Content: filter.Value.Phone.Content}
		case FilterOperatorIsNotEqual:
			ret.Phone = &ValuePhone{Content: filter.Value.Phone.Content + ""}
		case FilterOperatorContains:
			ret.Phone = &ValuePhone{Content: filter.Value.Phone.Content}
		case FilterOperatorDoesNotContain:
			ret.Phone = &ValuePhone{Content: ""}
		case FilterOperatorStartsWith:
			ret.Phone = &ValuePhone{Content: filter.Value.Phone.Content}
		case FilterOperatorEndsWith:
			ret.Phone = &ValuePhone{Content: filter.Value.Phone.Content}
		case FilterOperatorIsEmpty:
			ret.Phone = &ValuePhone{Content: ""}
		case FilterOperatorIsNotEmpty:
			ret.Phone = &ValuePhone{Content: ""}
		}
	case KeyTypeMAsset:
		switch filter.Operator {
		case FilterOperatorIsEqual, FilterOperatorContains:
			if 0 < len(filter.Value.MAsset) {
				ret.MAsset = []*ValueAsset{{Type: filter.Value.MAsset[0].Type, Name: filter.Value.MAsset[0].Name, Content: filter.Value.MAsset[0].Content}}
			}
		case FilterOperatorIsNotEqual, FilterOperatorDoesNotContain:
		case FilterOperatorIsEmpty:
			ret.MAsset = []*ValueAsset{}
		case FilterOperatorIsNotEmpty:
		}
	case KeyTypeCheckbox:
		switch filter.Operator {
		case FilterOperatorIsTrue:
			ret.Checkbox = &ValueCheckbox{Checked: true}
		case FilterOperatorIsFalse:
			ret.Checkbox = &ValueCheckbox{Checked: false}
		}
	case KeyTypeRelation:
		switch filter.Operator {
		case FilterOperatorContains:
			if 0 < len(filter.Value.Relation.Contents) {
				ret.Relation = &ValueRelation{Contents: filter.Value.Relation.Contents}
			}
		case FilterOperatorDoesNotContain:
		case FilterOperatorIsEmpty:
			ret.Relation = &ValueRelation{Contents: []*Value{}}
		case FilterOperatorIsNotEmpty:
		}
	}
	return
}
