package netmap

import (
	"strings"

	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/netmap"
)

type tempPlacementPolicy struct {
	BackupFactor uint32         `yaml:"containerBackupFactor"`
	Filters      []tempFilter   `yaml:"filters"`
	Selectors    []tempSelector `yaml:"selectors"`
	Replicas     []tempReplica  `yaml:"replicas"`
	Unique       bool           `yaml:"unique"`
}

type tempFilter struct {
	Name    string       `yaml:"name"`
	Key     string       `yaml:"key"`
	Op      string       `yaml:"op"`
	Value   string       `yaml:"value"`
	Filters []tempFilter `yaml:"filters"`
}

type tempSelector struct {
	Name      string `yaml:"name"`
	Count     uint32 `yaml:"count"`
	Clause    string `yaml:"clause"`
	Attribute string `yaml:"attribute"`
	Filter    string `yaml:"filter"`
}

type tempReplica struct {
	Count    uint32 `yaml:"count"`
	Selector string `yaml:"selector"`
}

func convertNFilters(temp []tempFilter) []netmap.Filter {
	var filters []netmap.Filter
	for _, tf := range temp {
		filters = append(filters, convertNFilter(tf))
	}
	return filters
}

var stringToOperationMap = map[string]netmap.Operation{
	"EQ":   netmap.EQ,
	"NE":   netmap.NE,
	"GT":   netmap.GT,
	"GE":   netmap.GE,
	"LT":   netmap.LT,
	"LE":   netmap.LE,
	"OR":   netmap.OR,
	"AND":  netmap.AND,
	"NOT":  netmap.NOT,
	"LIKE": netmap.LIKE,
}

func convertStringToOperation(opStr string) netmap.Operation {
	opStr = strings.ToUpper(opStr)
	if op, exists := stringToOperationMap[opStr]; exists {
		return op
	}
	return netmap.UnspecifiedOperation
}

func convertStringToClause(clauseStr string) netmap.Clause {
	switch strings.ToUpper(clauseStr) {
	case "DISTINCT":
		return netmap.Distinct
	default:
		return netmap.Same
	}
}

func convertNFilter(temp tempFilter) netmap.Filter {
	filter := netmap.Filter{}
	filter.SetKey(temp.Key)
	filter.SetName(temp.Name)
	filter.SetValue(temp.Value)
	filter.SetOp(convertStringToOperation(temp.Op))

	if temp.Filters != nil {
		filter.SetFilters(convertNFilters(temp.Filters))
	}
	return filter
}

func (p *PlacementPolicy) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var temp tempPlacementPolicy
	if err := unmarshal(&temp); err != nil {
		return err
	}

	for _, ts := range temp.Filters {
		netmapFilters := convertNFilter(ts)
		p.AddFilters(Filter{m: netmapFilters})
	}

	for _, ts := range temp.Selectors {
		selector := Selector{}
		selector.SetName(ts.Name)
		selector.SetNumberOfNodes(ts.Count)
		selector.SetClause(convertStringToClause(ts.Clause))
		selector.SelectByBucketAttribute(ts.Attribute)
		selector.SetFilterName(ts.Filter)
		p.AddSelectors(selector)
	}

	for _, tr := range temp.Replicas {
		replica := ReplicaDescriptor{}
		replica.SetSelectorName(tr.Selector)
		replica.m.SetCount(tr.Count)
		p.AddReplicas(replica)
	}

	p.SetContainerBackupFactor(temp.BackupFactor)
	p.SetUnique(temp.Unique)

	return nil
}

type Attribute struct {
	Key   string `yaml:"key"`
	Value string `yaml:"value"`
}

type tempNode struct {
	Attributes []Attribute `yaml:"attributes"`
}

func (x *NodeInfo) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var temp tempNode
	if err := unmarshal(&temp); err != nil {
		return err
	}

	for _, atr := range temp.Attributes {
		x.SetAttribute(atr.Key, atr.Value)
	}

	return nil
}