package auth

import (
	"git.mstar.dev/mstar/goutils/sliceutils"
	"github.com/rs/zerolog/log"

	"git.mstar.dev/mstar/linstrom/storage"
)

// Can actorId read the account with targetId?
func (a *Authentication) CanReadAccount(actorId *string, targetId string) bool {
	targetAccount, err := a.store.FindAccountById(targetId)
	if err != nil {
		if err == storage.ErrEntryNotFound {
			return true
		}
		log.Error().
			Err(err).
			Str("account-id", targetId).
			Msg("Failed to receive account for permission check")
		return false
	}
	if actorId == nil {
		// TODO: Decide if roles should have a field to declare an account as follow only/hidden
		// and then check for that flag here
		return true
	}
	roles, err := a.store.FindRolesByNames(targetAccount.Roles)
	if err != nil {
		log.Error().
			Err(err).
			Strs("role-names", targetAccount.Roles).
			Msg("Failed to get roles for target account")
		return false
	}
	combined := storage.CollapseRolesIntoOne(roles...)
	return !sliceutils.Contains(combined.BlockedUsers, *actorId)
}

// Can actorId edit the account with targetId?
// If actorId is nil, it is assumed to be an anonymous user trying to edit the target account
// if targetId is nil, it is assumed that the actor is editing themselves
func (a *Authentication) CanEditAccount(actorId *string, targetId *string) bool {
	// WARN: This entire function feels wrong, idk
	// Only the owner of an account should be able to edit said account's data
	// But how do moderation actions play with this? Do they count as edit or as something separate?
	if actorId == nil {
		return false
	}
	if targetId == nil {
		targetId = actorId
	}
	targetAccount, err := a.store.FindAccountById(*targetId)
	if err != nil {
		if err != storage.ErrEntryNotFound {
			log.Error().
				Err(err).
				Str("target-id", *targetId).
				Msg("Failed to receive account for permission checks")
		}
		return false
	}
	if targetId == actorId {
		targetRoles, err := a.store.FindRolesByNames(targetAccount.Roles)
		if err != nil {
			log.Error().
				Err(err).
				Strs("role-names", targetAccount.Roles).
				Msg("Failed to get roles from storage")
			return false
		}
		combined := storage.CollapseRolesIntoOne(targetRoles...)
		return *combined.CanLogin
	} else {
		return false
	}
}

// Can actorId delete the account with targetId?
// If actorId is nil, it is assumed to be an anonymous user trying to delete the target account
// if targetId is nil, it is assumed that the actor is deleting themselves
func (a *Authentication) CanDeleteAccount(actorId *string, targetId *string) bool {
	if actorId == nil {
		return false
	}
	acc, err := a.store.FindAccountById(*actorId)
	if err != nil {
		// TODO: Logging
		return false
	}
	roles, err := a.store.FindRolesByNames(acc.Roles)
	if err != nil {
		// TODO: Logging
		return false
	}
	collapsed := storage.CollapseRolesIntoOne(roles...)
	if targetId == nil {
		return *collapsed.CanLogin
	} else {
		return *collapsed.CanDeleteAccounts
	}
}

// Can actorId create a new post at all?
// Specific restrictions regarding the content are not checked
func (a *Authentication) CanCreatePost(actorId string) bool { return true }

// Ensures that a given post conforms with all roles attached to the author account.
// Returns the conforming note (or nil of it can't be changed to conform)
// and whether the note was changed
func (a *Authentication) EnsureNoteConformsWithRoles(note *storage.Note) (*storage.Note, bool) {
	return note, false
}

// Does the given note conform with the roles attached to the author account?
func (a *Authentication) DoesNoteConform(note *storage.Note) bool { return true }
