//go:build !openbsd && !plan9
// +build !openbsd,!plan9

package local

import (
	"fmt"
	"strings"
	"sync/atomic"
	"syscall"

	"github.com/pkg/xattr"
	"github.com/rclone/rclone/fs"
)

const (
	xattrPrefix    = "user." // FIXME is this correct for all unixes?
	xattrSupported = xattr.XATTR_SUPPORTED
)

// Check to see if the error supplied is a not supported error, and if
// so, disable xattrs
func (f *Fs) xattrIsNotSupported(err error) bool {
	xattrErr, ok := err.(*xattr.Error)
	if !ok {
		return false
	}
	// Xattrs not supported can be ENOTSUP or ENOATTR or EINVAL (on Solaris)
	if xattrErr.Err == syscall.EINVAL || xattrErr.Err == syscall.ENOTSUP || xattrErr.Err == xattr.ENOATTR {
		// Show xattrs not supported
		if atomic.CompareAndSwapInt32(&f.xattrSupported, 1, 0) {
			fs.Errorf(f, "xattrs not supported - disabling: %v", err)
		}
		return true
	}
	return false
}

// getXattr returns the extended attributes for an object
//
// It doesn't return any attributes owned by this backend in
// metadataKeys
func (o *Object) getXattr() (metadata fs.Metadata, err error) {
	if !xattrSupported || atomic.LoadInt32(&o.fs.xattrSupported) == 0 {
		return nil, nil
	}
	var list []string
	if o.fs.opt.FollowSymlinks {
		list, err = xattr.List(o.path)
	} else {
		list, err = xattr.LList(o.path)
	}
	if err != nil {
		if o.fs.xattrIsNotSupported(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("failed to read xattr: %w", err)
	}
	if len(list) == 0 {
		return nil, nil
	}
	metadata = make(fs.Metadata, len(list))
	for _, k := range list {
		var v []byte
		if o.fs.opt.FollowSymlinks {
			v, err = xattr.Get(o.path, k)
		} else {
			v, err = xattr.LGet(o.path, k)
		}
		if err != nil {
			if o.fs.xattrIsNotSupported(err) {
				return nil, nil
			}
			return nil, fmt.Errorf("failed to read xattr key %q: %w", k, err)
		}
		k = strings.ToLower(k)
		if !strings.HasPrefix(k, xattrPrefix) {
			continue
		}
		k = k[len(xattrPrefix):]
		if _, found := systemMetadataInfo[k]; found {
			continue
		}
		metadata[k] = string(v)
	}
	return metadata, nil
}

// setXattr sets the metadata on the file Xattrs
//
// It doesn't set any attributes owned by this backend in metadataKeys
func (o *Object) setXattr(metadata fs.Metadata) (err error) {
	if !xattrSupported || atomic.LoadInt32(&o.fs.xattrSupported) == 0 {
		return nil
	}
	for k, value := range metadata {
		k = strings.ToLower(k)
		if _, found := systemMetadataInfo[k]; found {
			continue
		}
		k = xattrPrefix + k
		v := []byte(value)
		if o.fs.opt.FollowSymlinks {
			err = xattr.Set(o.path, k, v)
		} else {
			err = xattr.LSet(o.path, k, v)
		}
		if err != nil {
			if o.fs.xattrIsNotSupported(err) {
				return nil
			}
			return fmt.Errorf("failed to set xattr key %q: %w", k, err)
		}
	}
	return nil
}