module Conduit.Features.Account.Common.QueryUserFollows (queryIfUserFollows) where

import Conduit.DB.Core (id2sqlKey)
import Conduit.DB.Utils (suchThat)
import Conduit.Features.Account.DB (Follow, User, UserId)
import Conduit.Features.Account.Types (UserID)
import Database.Esqueleto.Experimental (Entity, SqlExpr, Value, exists, from, just, table, val, (&&.), (==.))

queryIfUserFollows :: (ComparableUserEntity a) => SqlExpr a -> Maybe UserID -> SqlExpr (Value Bool)
queryIfUserFollows :: forall a.
ComparableUserEntity a =>
SqlExpr a -> Maybe UserID -> SqlExpr (Value Bool)
queryIfUserFollows SqlExpr a
follower Maybe UserID
followedID = SqlQuery () -> SqlExpr (Value Bool)
exists 
  (SqlQuery () -> SqlExpr (Value Bool))
-> SqlQuery () -> SqlExpr (Value Bool)
forall a b. (a -> b) -> a -> b
$ SqlQuery (SqlExpr (Entity Follow)) -> SqlQuery ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void 
  (SqlQuery (SqlExpr (Entity Follow)) -> SqlQuery ())
-> SqlQuery (SqlExpr (Entity Follow)) -> SqlQuery ()
forall a b. (a -> b) -> a -> b
$ From (SqlExpr (Entity Follow))
-> SqlQuery (SqlExpr (Entity Follow))
forall a a'. ToFrom a a' => a -> SqlQuery a'
from (forall ent. PersistEntity ent => From (SqlExpr (Entity ent))
table @Follow)
    SqlQuery (SqlExpr (Entity Follow))
-> (SqlExpr (Entity Follow) -> SqlExpr (Value Bool))
-> SqlQuery (SqlExpr (Entity Follow))
forall a. SqlQuery a -> (a -> SqlExpr (Value Bool)) -> SqlQuery a
`suchThat` \SqlExpr (Entity Follow)
f ->
      (SqlExpr (Value (Key User)) -> SqlExpr (Value (Maybe (Key User)))
forall typ. SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ))
just SqlExpr (Entity Follow)
f.followedID SqlExpr (Value (Maybe (Key User)))
-> SqlExpr (Value (Maybe (Key User))) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
==. Maybe (Key User) -> SqlExpr (Value (Maybe (Key User)))
forall typ. PersistField typ => typ -> SqlExpr (Value typ)
val (Maybe UserID
followedID Maybe UserID -> (UserID -> Key User) -> Maybe (Key User)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> UserID -> Key User
forall t id. SqlKey t id => id -> Key t
id2sqlKey)) SqlExpr (Value Bool)
-> SqlExpr (Value Bool) -> SqlExpr (Value Bool)
&&. (SqlExpr a
follower SqlExpr a -> SqlExpr (Value (Key User)) -> SqlExpr (Value Bool)
forall a.
ComparableUserEntity a =>
SqlExpr a -> SqlExpr (Value (Key User)) -> SqlExpr (Value Bool)
`compIDWith` SqlExpr (Entity Follow)
f.followerID)

-- Not sure if I'm being stupid, but I can't find a method that turns an Entity inside SqlExpr into a Maybe
-- so this is just a quick little workaround "for now"

class ComparableUserEntity a where
  compIDWith :: SqlExpr a -> SqlExpr (Value UserId) -> SqlExpr (Value Bool)

instance ComparableUserEntity (Entity User) where
  compIDWith :: SqlExpr (Entity User) -> SqlExpr (Value UserId) -> SqlExpr (Value Bool)
  compIDWith :: SqlExpr (Entity User)
-> SqlExpr (Value (Key User)) -> SqlExpr (Value Bool)
compIDWith SqlExpr (Entity User)
u SqlExpr (Value (Key User))
otherID = SqlExpr (Value (Key User))
otherID SqlExpr (Value (Key User))
-> SqlExpr (Value (Key User)) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
==. SqlExpr (Entity User)
u.id

instance ComparableUserEntity (Maybe (Entity User)) where
  compIDWith :: SqlExpr (Maybe (Entity User)) -> SqlExpr (Value UserId) -> SqlExpr (Value Bool)
  compIDWith :: SqlExpr (Maybe (Entity User))
-> SqlExpr (Value (Key User)) -> SqlExpr (Value Bool)
compIDWith SqlExpr (Maybe (Entity User))
u SqlExpr (Value (Key User))
otherID = SqlExpr (Value (Key User)) -> SqlExpr (Value (Maybe (Key User)))
forall typ. SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ))
just SqlExpr (Value (Key User))
otherID SqlExpr (Value (Maybe (Key User)))
-> SqlExpr (Value (Maybe (Key User))) -> SqlExpr (Value Bool)
forall typ.
PersistField typ =>
SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
==. SqlExpr (Maybe (Entity User))
u.id