package ape

import (
	"errors"
	"fmt"
	"strings"

	"git.frostfs.info/TrueCloudLab/frostfs-node/internal/ape"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
	"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
)

var (
	ErrInvalidResource    = errors.New("invalid resource name")
	ErrUnsupportedPrefix  = errors.New("unsupported resource name prefix")
	ErrInvalidContainerID = errors.New("invalid container id")
	ErrInvalidObjectID    = errors.New("invalid object id")
	ErrInvalidNamespace   = fmt.Errorf("namespace must match regexp: %s", ape.NamespaceNameRegexp.String())
)

// ValidateResourceName validates resource name components - container and object id, namespace.
// Also validates matching resource name to templates of policy engine's native scheme.
func ValidateResourceName(name string) error {
	if after, found := strings.CutPrefix(name, native.ObjectPrefix+"/"); found {
		return validateObjectResourceName(after)
	} else if after, found = strings.CutPrefix(name, native.ContainerPrefix+"/"); found {
		return validateContainerResourceName(after)
	}
	return ErrUnsupportedPrefix
}

// validateObjectResourceName validate name for object.
// Name should be without prefix `native.ObjectPrefix`.
func validateObjectResourceName(name string) error {
	if name == "*" {
		return nil
	}
	lexems := strings.Split(name, "/")
	if len(lexems) == 1 && lexems[0] == "*" {
		return nil
	} else if len(lexems) == 2 {
		// len == 2 means format `namespace(root_namespace)/*`
		if lexems[0] != "" && !ape.NamespaceNameRegexp.MatchString(lexems[0]) {
			return ErrInvalidNamespace
		}
		if lexems[1] == "*" {
			return nil
		}
	} else if len(lexems) == 3 {
		// len == 3 means format `namespace(root_namespace)/CID/OID(*)`
		if lexems[0] != "" && !ape.NamespaceNameRegexp.MatchString(lexems[0]) {
			return ErrInvalidNamespace
		}
		var cnr cid.ID
		err := cnr.DecodeString(lexems[1])
		if err != nil {
			return fmt.Errorf("%w: %w", ErrInvalidContainerID, err)
		}
		if lexems[2] == "*" {
			return nil
		}
		var objID oid.ID
		err = objID.DecodeString(lexems[2])
		if err != nil {
			return fmt.Errorf("%w: %w", ErrInvalidObjectID, err)
		}
		return nil
	}
	return ErrInvalidResource
}

// validateContainerResourceName validate resource name for container.
// Name should be without prefix `native.ContainerPrefix`.
func validateContainerResourceName(name string) error {
	if name == "*" {
		return nil
	}
	lexems := strings.Split(name, "/")
	if len(lexems) == 1 && lexems[0] == "*" {
		return nil
	} else if len(lexems) == 2 {
		// len == 2 means format `namespace(root_namespace)/CID(*)`
		if lexems[0] != "" && !ape.NamespaceNameRegexp.MatchString(lexems[0]) {
			return ErrInvalidNamespace
		}
		if lexems[1] != "*" {
			var cnr cid.ID
			err := cnr.DecodeString(lexems[1])
			if err != nil {
				return fmt.Errorf("%w: %w", ErrInvalidContainerID, err)
			}
		}
		return nil
	}
	return ErrInvalidResource
}