package audit

import (
	"strings"

	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)

type ModelType[T any] interface {
	ReadFromV2(m T) error
	String() string
}

func TargetFromRef[T any](ref *T, model ModelType[T]) Target {
	if ref == nil {
		return stringTarget{s: NotDefined}
	}
	if err := model.ReadFromV2(*ref); err != nil {
		return stringTarget{s: InvalidValue}
	}
	return stringTarget{s: model.String()}
}

func TargetFromRefs[T any](refs []*T, model ModelType[T]) Target {
	if len(refs) == 0 {
		return stringTarget{s: NotDefined}
	}
	sb := &strings.Builder{}
	for idx, ref := range refs {
		if idx > 0 {
			sb.WriteString(";")
		}
		if ref == nil {
			sb.WriteString(Empty)
			continue
		}
		if err := model.ReadFromV2(*ref); err != nil {
			sb.WriteString(InvalidValue)
		} else {
			sb.WriteString(model.String())
		}
	}
	return sb
}

type stringTarget struct {
	s string
}

func (t stringTarget) String() string {
	return t.s
}

func TargetFromString(s string) Target {
	if len(s) == 0 {
		s = Empty
	}
	return stringTarget{s: s}
}

func TargetFromChainID(chainTargetType, chainTargetName string, chainID []byte) Target {
	if len(chainTargetType) == 0 && len(chainTargetName) == 0 && len(chainID) == 0 {
		return stringTarget{s: NotDefined}
	}
	t, n, c := Empty, Empty, Empty
	if len(chainTargetType) > 0 {
		t = chainTargetType
	}
	if len(chainTargetName) > 0 {
		n = chainTargetName
	}
	if len(chainID) > 0 {
		c = string(chainID)
	}
	return stringTarget{s: t + ":" + n + ":" + c}
}

func TargetFromContainerIDObjectID(containerID *refs.ContainerID, objectID *refs.ObjectID) Target {
	if containerID == nil && objectID == nil {
		return stringTarget{s: NotDefined}
	}
	c, o := Empty, Empty
	if containerID != nil {
		var cnr cid.ID
		if err := cnr.ReadFromV2(*containerID); err != nil {
			c = InvalidValue
		} else {
			c = cnr.EncodeToString()
		}
	}
	if objectID != nil {
		var obj oid.ID
		if err := obj.ReadFromV2(*objectID); err != nil {
			o = InvalidValue
		} else {
			o = obj.EncodeToString()
		}
	}
	return stringTarget{s: c + "/" + o}
}