{-# LANGUAGE ConstrainedClassMethods #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Data.Morpheus.App.Internal.Resolving.MonadResolver
  ( MonadResolver (..),
    MonadIOResolver,
    SubscriptionField (..),
    ResolverContext (..),
    withArguments,
    getArgument,
  )
where

import Control.Monad.Except (MonadError)
import Data.Morpheus.App.Internal.Resolving.Event
  ( EventHandler (..),
    ResponseEvent,
  )
import Data.Morpheus.App.Internal.Resolving.ResolverState
  ( ResolverContext (..),
    ResolverState,
  )
import Data.Morpheus.Internal.Ext (ResultT)
import Data.Morpheus.Internal.Utils (selectOr)
import Data.Morpheus.Types.Internal.AST
  ( Argument (..),
    Arguments,
    FieldName,
    GQLError,
    MUTATION,
    OperationType,
    SUBSCRIPTION,
    Selection,
    VALID,
    ValidValue,
    Value (..),
  )
import Relude

class (MonadResolver m, MonadIO m) => MonadIOResolver (m :: Type -> Type)

class
  ( Monad m,
    MonadReader ResolverContext m,
    MonadFail m,
    MonadError GQLError m,
    Monad (MonadParam m)
  ) =>
  MonadResolver (m :: Type -> Type)
  where
  type MonadOperation m :: OperationType
  type MonadEvent m :: Type
  type MonadQuery m :: (Type -> Type)
  type MonadMutation m :: (Type -> Type)
  type MonadSubscription m :: (Type -> Type)
  type MonadParam m :: (Type -> Type)
  liftState :: ResolverState a -> m a
  getArguments :: m (Arguments VALID)
  subscribe :: (MonadOperation m ~ SUBSCRIPTION) => Channel (MonadEvent m) -> MonadQuery m (MonadEvent m -> m a) -> SubscriptionField (m a)
  publish :: (MonadOperation m ~ MUTATION) => [MonadEvent m] -> m ()
  runResolver ::
    Maybe (Selection VALID -> ResolverState (Channel (MonadEvent m))) ->
    m ValidValue ->
    ResolverContext ->
    ResponseStream (MonadEvent m) (MonadParam m) ValidValue

data SubscriptionField (a :: Type) where
  SubscriptionField ::
    { channel :: forall m v. (a ~ m v, MonadResolver m, MonadOperation m ~ SUBSCRIPTION) => Channel (MonadEvent m),
      unSubscribe :: a
    } ->
    SubscriptionField a

withArguments :: (MonadResolver m) => (Arguments VALID -> m a) -> m a
withArguments = (getArguments >>=)

getArgument :: (MonadResolver m) => FieldName -> m (Value VALID)
getArgument name = selectOr Null argumentValue name <$> getArguments

type ResponseStream event (m :: Type -> Type) = ResultT (ResponseEvent event m) m
