module Database.PostgreSQL.Query.SqlBuilder.Builder
       ( SqlBuilder(..)
         -- * Running
       , runSqlBuilder
         -- * Building
       , emptyB
       , mkValue
       , mkMaskedValue
       , sqlBuilderFromField
       -- ** Unsafe
       , sqlBuilderPure
       , sqlBuilderFromByteString
       ) where

import Blaze.ByteString.Builder (Builder)
import Data.ByteString (ByteString)
import Data.Semigroup
import Data.String
import Data.Typeable
import Database.PostgreSQL.Query.SqlBuilder.Types
import Database.PostgreSQL.Simple
import Database.PostgreSQL.Simple.Internal
import Database.PostgreSQL.Simple.ToField
import Database.PostgreSQL.Simple.Types
import GHC.Generics (Generic)

#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif

import qualified Blaze.ByteString.Builder as BB
import qualified Blaze.ByteString.Builder.Char.Utf8 as BB


-- | Builder wich can be effectively concatenated. Requires 'Connection'
-- inside for string quoting implemented in __libpq__. Builds two strings: query
-- string and log string which may differ.
newtype SqlBuilder = SqlBuilder
  { sqlBuild :: Connection -> LogMasker -> IO SqlBuilderResult
  } deriving (Typeable, Generic)

instance Semigroup SqlBuilder where
  (SqlBuilder a) <> (SqlBuilder b) =
    SqlBuilder $ \c masker -> (<>) <$> (a c masker) <*> (b c masker)

instance Monoid SqlBuilder where
  mempty = sqlBuilderPure mempty
  mappend = (<>)

instance IsString SqlBuilder where
  fromString s = SqlBuilder $ \_ _ -> return $ builderResultPure $ BB.fromString s

-- | Returns query string with log bytestring
runSqlBuilder :: Connection -> LogMasker -> SqlBuilder -> IO (Query, ByteString)
runSqlBuilder con masker (SqlBuilder bld) = toTuple <$> bld con masker
  where
    toTuple res =
      ( Query $ BB.toByteString $ sbQueryString res
      , BB.toByteString $ sbLogString res )

-- | Typed synonym of 'mempty'
emptyB :: SqlBuilder
emptyB = mempty

-- | Shorthand function to convert single field value to builder
mkValue :: (ToField a) => a -> SqlBuilder
mkValue = sqlBuilderFromField FieldDefault

-- | Shorthand function to convert single masked field value (which should not
-- be shown in log)
mkMaskedValue :: (ToField a) => a -> SqlBuilder
mkMaskedValue = sqlBuilderFromField FieldMasked

sqlBuilderFromField :: (ToField a) => FieldOption -> a -> SqlBuilder
sqlBuilderFromField fo field = SqlBuilder $ \con masker -> do
  qbs <- buildAction con "" [] $ toField field
  let sbQueryString = qbs
      sbLogString   = masker fo qbs
  return SqlBuilderResult{..}

-- | Lift pure bytestring builder to 'SqlBuilder'. This is unsafe to use
-- directly in your code.
sqlBuilderPure :: Builder -> SqlBuilder
sqlBuilderPure b = SqlBuilder $ \_ _ -> pure $ builderResultPure b

-- | Unsafe function to make SqlBuilder from arbitrary ByteString. Does not
-- perform any checks. Dont use it directly in your code unless you know what
-- you are doing.
sqlBuilderFromByteString :: ByteString -> SqlBuilder
sqlBuilderFromByteString = sqlBuilderPure . BB.fromByteString