-- | Important: it is required that the Hashable and Eq instances used for HashSet respect
-- whatever is used for serialization, since Redis will decide equality only on serialized values.

module HLRDB.Structures.Set where

import Data.Hashable
import Data.HashSet as S
import Database.Redis as Redis
import HLRDB.Primitives.Redis
import HLRDB.Internal


-- | Retrieve the elements of a set from Redis
smembers :: (MonadRedis m , Eq b , Hashable b) => RedisSet a b -> a -> m (HashSet b)
smembers p@(RSet (E _ _ d)) =
    fmap (S.fromList . fmap (d . pure))
  . unwrap
  . Redis.smembers
  . primKey p

-- | Test if an item is a member of a set
sismember :: MonadRedis m => RedisSet a b -> a -> b -> m Bool
sismember p@(RSet (E _ e _)) k =
    unwrap
  . Redis.sismember (primKey p k)
  . runIdentity
  . e

-- | Add items to a set
sadd :: (MonadRedis m , Traversable t) => RedisSet a b -> a -> t b -> m ()
sadd p@(RSet (E _ e _)) k =
  fixEmpty (ignore . unwrap . Redis.sadd (primKey p k)) (runIdentity . e)

-- | Remove items from a set
srem :: (MonadRedis m , Traversable t) => RedisSet a b -> a -> t b -> m ()
srem p@(RSet (E _ e _)) k =
  fixEmpty (ignore . unwrap . Redis.srem (primKey p k)) (runIdentity . e)

-- | Retrieve the cardinality of a set
scard :: MonadRedis m => RedisSet a b -> a -> m Integer
scard p =
    unwrap
  . Redis.scard
  . primKey p

-- | Retrieve a random element from a set. The underlying Redis primitive uses a poor but efficient distribution, biased by the underlying hash bucket allocation.
srandmember :: MonadRedis m => RedisSet a b -> a -> m (Maybe b)
srandmember p@(RSet (E _ _ d)) =
    (fmap . fmap) (d . pure)
  . unwrap
  . Redis.srandmember
  . primKey p

-- | Retrieve up to N unique random elements, limited by the cardinality of the set.
srandmemberN :: MonadRedis m => RedisSet a b -> Integer -> a -> m [ b ]
srandmemberN p@(RSet (E _ _ d)) n =
    (fmap . fmap) (d . pure)
  . unwrap
  . flip Redis.srandmemberN n
  . primKey p

-- | Use a cursor to iterate a collection
sscan :: MonadRedis m => RedisSet a b -> a -> Cursor -> m (Maybe Cursor , [ b ])
sscan p@(RSet (E _ _ d)) k =
    unwrapCursor (fmap (d . pure))
  . Redis.sscan (primKey p k)