module AirGQL.ServerUtils (
  executeQuery,
) where

import Protolude (
  Applicative (pure),
  Either (Left, Right),
  FilePath,
  IO,
  Maybe (Just, Nothing),
  toList,
  ($),
  (&),
  (<&>),
 )
import Protolude qualified as P

import Conduit (sourceToList)
import Control.Arrow ((>>>))
import Data.Aeson (Object, Value (String))
import Data.Text (Text)
import Data.Text qualified as T
import Database.SQLite.Simple qualified as SS
import Language.GraphQL.Error (Error (Error), Response (Response))
import Language.GraphQL.JSON (graphql)
import System.FilePath (pathSeparator, (</>))

import AirGQL.GraphQL (getDerivedSchema)
import AirGQL.Lib (getTables)
import AirGQL.Types.SchemaConf (SchemaConf)
import AirGQL.Types.Types (
  GQLResponse (GQLResponse, data_, errors),
  gqlResponseToObject,
 )


executeQuery
  :: SchemaConf
  -> Text
  -> FilePath
  -> Text
  -> Object
  -> Maybe Text
  -> IO Object
executeQuery schemaConf dbIdOrPath reqDir query vars opNameMb = do
  let dbFilePath =
        if pathSeparator `T.elem` dbIdOrPath
          then T.unpack dbIdOrPath
          else reqDir </> "main.sqlite"

  theConn <- SS.open dbFilePath
  tables <- getTables theConn
  schema <- getDerivedSchema schemaConf theConn dbIdOrPath tables
  result <- graphql schema opNameMb vars query
  SS.close theConn

  case result of
    Left errMsg -> do
      errors <- sourceToList errMsg
      pure $
        gqlResponseToObject $
          GQLResponse
            { data_ = Nothing
            , errors =
                Just $
                  errors
                    <&> ((\(Response _ errs) -> errs) >>> toList)
                    & P.concat
                    <&> (\(Error msg _ _) -> String msg)
            }
    Right response -> pure response