{-# LANGUAGE OverloadedStrings #-}

module Data.Morpheus.Parser.Operator
  ( parseAnonymousQuery
  , parseOperator
  ) where

import           Data.Functor                              (($>))
import           Data.Morpheus.Parser.Body                 (entries)
import           Data.Morpheus.Parser.Internal             (Parser)
import           Data.Morpheus.Parser.Primitive            (token, variable)
import           Data.Morpheus.Parser.Terms                (nonNull, parseAssignment, parseMaybeTuple)
import           Data.Morpheus.Types.Internal.AST.Operator (Operator (..), Operator' (..), RawOperator, RawOperator',
                                                            Variable (..))
import           Data.Morpheus.Types.Internal.Data         (DataTypeWrapper (..))
import           Data.Text                                 (Text)
import           Text.Megaparsec                           (between, getSourcePos, label, (<?>), (<|>))
import           Text.Megaparsec.Char                      (char, space, space1, string)

wrapMock :: Parser ([DataTypeWrapper], Text)
wrapMock = do
  mock <- token
  space
  return ([], mock)

insideList :: Parser ([DataTypeWrapper], Text)
insideList =
  between
    (char '[' *> space)
    (char ']' *> space)
    (do (list, name) <- wrapMock <|> insideList
        nonNull' <- nonNull
        return ((ListType : nonNull') ++ list, name))

wrappedSignature :: Parser ([DataTypeWrapper], Text)
wrappedSignature = do
  sig <- insideList <|> wrapMock
  space
  return sig

operatorArgument :: Parser (Text, Variable ())
operatorArgument =
  label "operatorArgument" $ do
    ((name', position'), (wrappers', type')) <- parseAssignment variable wrappedSignature
    nonNull' <- nonNull
    pure
      ( name'
      , Variable
          { variableType = type'
          , isVariableRequired = 0 < length nonNull'
          , variableTypeWrappers = nonNull' ++ wrappers'
          , variablePosition = position'
          , variableValue = ()
          })

parseOperator :: Parser RawOperator
parseOperator =
  label "operator" $ do
    pos <- getSourcePos
    kind' <- operatorKind
    operatorName' <- token
    variables <- parseMaybeTuple operatorArgument
    sel <- entries
    pure (kind' $ Operator' operatorName' variables sel pos)

parseAnonymousQuery :: Parser RawOperator
parseAnonymousQuery =
  label "AnonymousQuery" $ do
    position' <- getSourcePos
    selection' <- entries
    pure (Query $ Operator' "" [] selection' position') <?> "can't parse AnonymousQuery"

operatorKind :: Parser (RawOperator' -> RawOperator)
operatorKind =
  label "operatorKind" $ do
    kind <- (string "query" $> Query) <|> (string "mutation" $> Mutation) <|> (string "subscription" $> Subscription)
    space1
    return kind