{-# LANGUAGE NamedFieldPuns    #-}
{-# LANGUAGE OverloadedStrings #-}

module Data.Morpheus.Parsing.Request.Operation
  (parseOperation
  ) where

import           Data.Functor                               (($>))
import           Data.Text                                  (Text)
import           Text.Megaparsec                            (label, optional, (<?>), (<|>))
import           Text.Megaparsec.Char                       (string)

--
-- MORPHEUS
import           Data.Morpheus.Parsing.Internal.Internal    (Parser, getLocation)
import           Data.Morpheus.Parsing.Internal.Pattern     (optionalDirectives)
import           Data.Morpheus.Parsing.Internal.Terms       (operator, parseMaybeTuple, parseName, parseType,
                                                             spaceAndComments1, variable)
import           Data.Morpheus.Parsing.Internal.Value       (parseDefaultValue)
import           Data.Morpheus.Parsing.Request.Selection    (parseSelectionSet)
import           Data.Morpheus.Types.Internal.AST.Operation (DefaultValue, Operation (..), RawOperation, Variable (..))
import           Data.Morpheus.Types.Internal.Data          (OperationType (..), isNullable)


-- Variables :  https://graphql.github.io/graphql-spec/June2018/#VariableDefinition
--
--  VariableDefinition
--    Variable : Type DefaultValue(opt)
--
variableDefinition :: Parser (Text, Variable DefaultValue)
variableDefinition =
  label "VariableDefinition" $ do
    (name, variablePosition) <- variable
    operator ':'
    (variableTypeWrappers, variableType) <- parseType
    defaultValue <- parseDefaultValue
    pure
      ( name
      , Variable
          { variableType
          , isVariableRequired = not (isNullable variableTypeWrappers)
          , variableTypeWrappers
          , variablePosition
          , variableValue = defaultValue
          })

-- Operations : https://graphql.github.io/graphql-spec/June2018/#sec-Language.Operations
--
-- OperationDefinition
--   OperationType Name(opt) VariableDefinitions(opt) Directives(opt) SelectionSet
--
--   OperationType: one of
--     query, mutation,    subscription
parseOperationDefinition :: Parser RawOperation
parseOperationDefinition =
  label "OperationDefinition" $ do
    operationPosition <- getLocation
    operationType <- parseOperationType
    operationName <- optional parseName
    operationArgs <- parseMaybeTuple variableDefinition
    -- TODO: handle directives
    _directives <- optionalDirectives
    operationSelection <- parseSelectionSet
    pure (Operation {operationName, operationType, operationArgs, operationSelection, operationPosition})

parseOperationType :: Parser OperationType
parseOperationType =
  label "OperationType" $ do
    kind <- (string "query" $> Query) <|> (string "mutation" $> Mutation) <|> (string "subscription" $> Subscription)
    spaceAndComments1
    return kind

parseAnonymousQuery :: Parser RawOperation
parseAnonymousQuery =
  label "AnonymousQuery" $ do
    operationPosition <- getLocation
    operationSelection <- parseSelectionSet
    pure
      (Operation
         { operationName = Nothing
         , operationType = Query
         , operationArgs = []
         , operationSelection
         , operationPosition
         }) <?>
      "can't parse AnonymousQuery"

parseOperation :: Parser RawOperation
parseOperation = parseAnonymousQuery <|> parseOperationDefinition