package config

import (
	"fmt"
	"strings"

	"github.com/containers/image/v5/internal/pkg/keyctl"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const keyDescribePrefix = "container-registry-login:"

func getAuthFromKernelKeyring(registry string) (string, string, error) {
	userkeyring, err := keyctl.UserKeyring()
	if err != nil {
		return "", "", err
	}
	key, err := userkeyring.Search(genDescription(registry))
	if err != nil {
		return "", "", err
	}
	authData, err := key.Get()
	if err != nil {
		return "", "", err
	}
	parts := strings.SplitN(string(authData), "\x00", 2)
	if len(parts) != 2 {
		return "", "", nil
	}
	return parts[0], parts[1], nil
}

func deleteAuthFromKernelKeyring(registry string) error {
	userkeyring, err := keyctl.UserKeyring()

	if err != nil {
		return err
	}
	key, err := userkeyring.Search(genDescription(registry))
	if err != nil {
		return err
	}
	return key.Unlink()
}

func removeAllAuthFromKernelKeyring() error {
	keys, err := keyctl.ReadUserKeyring()
	if err != nil {
		return err
	}

	userkeyring, err := keyctl.UserKeyring()
	if err != nil {
		return err
	}

	for _, k := range keys {
		keyAttr, err := k.Describe()
		if err != nil {
			return err
		}
		// split string "type;uid;gid;perm;description"
		keyAttrs := strings.SplitN(keyAttr, ";", 5)
		if len(keyAttrs) < 5 {
			return errors.Errorf("Key attributes of %d are not avaliable", k.ID())
		}
		keyDescribe := keyAttrs[4]
		if strings.HasPrefix(keyDescribe, keyDescribePrefix) {
			err := keyctl.Unlink(userkeyring, k)
			if err != nil {
				return errors.Wrapf(err, "error unlinking key %d", k.ID())
			}
			logrus.Debugf("unlinked key %d:%s", k.ID(), keyAttr)
		}
	}
	return nil
}

func setAuthToKernelKeyring(registry, username, password string) error {
	keyring, err := keyctl.SessionKeyring()
	if err != nil {
		return err
	}
	id, err := keyring.Add(genDescription(registry), []byte(fmt.Sprintf("%s\x00%s", username, password)))
	if err != nil {
		return err
	}

	// sets all permission(view,read,write,search,link,set attribute) for current user
	// it enables the user to search the key after it linked to user keyring and unlinked from session keyring
	err = keyctl.SetPerm(id, keyctl.PermUserAll)
	if err != nil {
		return err
	}
	// link the key to userKeyring
	userKeyring, err := keyctl.UserKeyring()
	if err != nil {
		return errors.Wrapf(err, "error getting user keyring")
	}
	err = keyctl.Link(userKeyring, id)
	if err != nil {
		return errors.Wrapf(err, "error linking the key to user keyring")
	}
	// unlink the key from session keyring
	err = keyctl.Unlink(keyring, id)
	if err != nil {
		return errors.Wrapf(err, "error unlinking the key from session keyring")
	}
	return nil
}

func genDescription(registry string) string {
	return fmt.Sprintf("%s%s", keyDescribePrefix, registry)
}
