{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Data.Morpheus.Client.Transform.Global
  ( toArgumentsType,
    toGlobalDefinitions,
  )
where

import Data.Morpheus.Client.Internal.Types
  ( ClientDeclaration,
    ClientTypeDefinition (..),
  )
import Data.Morpheus.Client.Transform.Core (toClientDeclarations, toCodeGenField)
import Data.Morpheus.CodeGen.Internal.AST
  ( CodeGenConstructor (..),
    CodeGenTypeName (..),
    fromTypeName,
  )
import Data.Morpheus.Internal.Utils
  ( empty,
  )
import Data.Morpheus.Types.Internal.AST
  ( ANY,
    DataEnumValue (DataEnumValue, enumName),
    FieldDefinition (..),
    RAW,
    Schema (Schema, types),
    TRUE,
    TypeContent (..),
    TypeDefinition (..),
    TypeKind (..),
    TypeName,
    VALID,
    Variable (..),
    VariableDefinitions,
    isNotSystemTypeName,
    isResolverType,
  )
import Relude hiding (empty)

toArgumentsType ::
  CodeGenTypeName ->
  VariableDefinitions RAW ->
  Maybe ClientTypeDefinition
toArgumentsType :: CodeGenTypeName
-> VariableDefinitions RAW -> Maybe ClientTypeDefinition
toArgumentsType CodeGenTypeName
clientTypeName VariableDefinitions RAW
variables
  | forall (t :: * -> *) a. Foldable t => t a -> Bool
null VariableDefinitions RAW
variables = forall a. Maybe a
Nothing
  | Bool
otherwise =
      forall a. a -> Maybe a
Just
        ClientTypeDefinition
          { CodeGenTypeName
clientTypeName :: CodeGenTypeName
clientTypeName :: CodeGenTypeName
clientTypeName,
            clientKind :: TypeKind
clientKind = TypeKind
KindInputObject,
            clientCons :: [CodeGenConstructor]
clientCons =
              [ CodeGenConstructor
                  { constructorName :: CodeGenTypeName
constructorName = CodeGenTypeName
clientTypeName,
                    constructorFields :: [CodeGenField]
constructorFields = forall (a :: TypeCategory) (b :: Stage).
FieldDefinition a b -> CodeGenField
toCodeGenField forall b c a. (b -> c) -> (a -> b) -> a -> c
. Variable RAW -> FieldDefinition ANY VALID
toFieldDefinition forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) a. Foldable t => t a -> [a]
toList VariableDefinitions RAW
variables
                  }
              ]
          }

toFieldDefinition :: Variable RAW -> FieldDefinition ANY VALID
toFieldDefinition :: Variable RAW -> FieldDefinition ANY VALID
toFieldDefinition Variable {FieldName
variableName :: forall (stage :: Stage). Variable stage -> FieldName
variableName :: FieldName
variableName, TypeRef
variableType :: forall (stage :: Stage). Variable stage -> TypeRef
variableType :: TypeRef
variableType} =
  FieldDefinition
    { fieldName :: FieldName
fieldName = FieldName
variableName,
      fieldContent :: Maybe (FieldContent TRUE ANY VALID)
fieldContent = forall a. Maybe a
Nothing,
      fieldType :: TypeRef
fieldType = TypeRef
variableType,
      fieldDescription :: Maybe Description
fieldDescription = forall a. Maybe a
Nothing,
      fieldDirectives :: Directives VALID
fieldDirectives = forall coll. Empty coll => coll
empty
    }

toGlobalDefinitions :: (TypeName -> Bool) -> Schema VALID -> [ClientDeclaration]
toGlobalDefinitions :: (TypeName -> Bool) -> Schema VALID -> [ClientDeclaration]
toGlobalDefinitions TypeName -> Bool
f Schema {TypeDefinitions VALID
types :: TypeDefinitions VALID
types :: forall (s :: Stage). Schema s -> TypeDefinitions s
types} =
  forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ClientTypeDefinition -> [ClientDeclaration]
toClientDeclarations forall a b. (a -> b) -> a -> b
$
    forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe TypeDefinition ANY VALID -> Maybe ClientTypeDefinition
generateGlobalType forall a b. (a -> b) -> a -> b
$
      forall a. (a -> Bool) -> [a] -> [a]
filter TypeDefinition ANY VALID -> Bool
shouldInclude (forall (t :: * -> *) a. Foldable t => t a -> [a]
toList TypeDefinitions VALID
types)
  where
    shouldInclude :: TypeDefinition ANY VALID -> Bool
shouldInclude TypeDefinition ANY VALID
t =
      Bool -> Bool
not (forall t. Strictness t => t -> Bool
isResolverType TypeDefinition ANY VALID
t)
        Bool -> Bool -> Bool
&& TypeName -> Bool
isNotSystemTypeName (forall (a :: TypeCategory) (s :: Stage).
TypeDefinition a s -> TypeName
typeName TypeDefinition ANY VALID
t)
        Bool -> Bool -> Bool
&& TypeName -> Bool
f (forall (a :: TypeCategory) (s :: Stage).
TypeDefinition a s -> TypeName
typeName TypeDefinition ANY VALID
t)

generateGlobalType :: TypeDefinition ANY VALID -> Maybe ClientTypeDefinition
generateGlobalType :: TypeDefinition ANY VALID -> Maybe ClientTypeDefinition
generateGlobalType TypeDefinition {TypeName
typeName :: TypeName
typeName :: forall (a :: TypeCategory) (s :: Stage).
TypeDefinition a s -> TypeName
typeName, TypeContent TRUE ANY VALID
typeContent :: forall (a :: TypeCategory) (s :: Stage).
TypeDefinition a s -> TypeContent TRUE a s
typeContent :: TypeContent TRUE ANY VALID
typeContent} = do
  (TypeKind
clientKind, [CodeGenConstructor]
clientCons) <- TypeContent TRUE ANY VALID
-> Maybe (TypeKind, [CodeGenConstructor])
genContent TypeContent TRUE ANY VALID
typeContent
  forall (f :: * -> *) a. Applicative f => a -> f a
pure
    ClientTypeDefinition
      { clientTypeName :: CodeGenTypeName
clientTypeName = TypeName -> CodeGenTypeName
fromTypeName TypeName
typeName,
        TypeKind
clientKind :: TypeKind
clientKind :: TypeKind
clientKind,
        [CodeGenConstructor]
clientCons :: [CodeGenConstructor]
clientCons :: [CodeGenConstructor]
clientCons
      }
  where
    genContent :: TypeContent TRUE ANY VALID -> Maybe (TypeKind, [CodeGenConstructor])
    genContent :: TypeContent TRUE ANY VALID
-> Maybe (TypeKind, [CodeGenConstructor])
genContent (DataInputObject FieldsDefinition IN VALID
inputFields) =
      forall (f :: * -> *) a. Applicative f => a -> f a
pure
        ( TypeKind
KindInputObject,
          [ CodeGenConstructor
              { constructorName :: CodeGenTypeName
constructorName = TypeName -> CodeGenTypeName
fromTypeName TypeName
typeName,
                constructorFields :: [CodeGenField]
constructorFields = forall (a :: TypeCategory) (b :: Stage).
FieldDefinition a b -> CodeGenField
toCodeGenField forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) a. Foldable t => t a -> [a]
toList FieldsDefinition IN VALID
inputFields
              }
          ]
        )
    genContent (DataEnum DataEnum VALID
enumTags) = forall (f :: * -> *) a. Applicative f => a -> f a
pure (TypeKind
KindEnum, forall (s :: Stage).
TypeName -> DataEnumValue s -> CodeGenConstructor
mkConsEnum TypeName
typeName forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DataEnum VALID
enumTags)
    genContent DataScalar {} = forall (f :: * -> *) a. Applicative f => a -> f a
pure (TypeKind
KindScalar, [])
    genContent TypeContent TRUE ANY VALID
_ = forall a. Maybe a
Nothing

mkConsEnum :: TypeName -> DataEnumValue s -> CodeGenConstructor
mkConsEnum :: forall (s :: Stage).
TypeName -> DataEnumValue s -> CodeGenConstructor
mkConsEnum TypeName
typename DataEnumValue {TypeName
enumName :: TypeName
enumName :: forall (s :: Stage). DataEnumValue s -> TypeName
enumName} =
  CodeGenTypeName -> [CodeGenField] -> CodeGenConstructor
CodeGenConstructor ([FieldName] -> [Description] -> TypeName -> CodeGenTypeName
CodeGenTypeName [coerce :: forall a b. Coercible a b => a -> b
coerce TypeName
typename] [] TypeName
enumName) []