module Database.PostgreSQL.Embedded.Postgres
    ( startPostgres
    , stopPostgres
    , checkPostgresStarted
    ) where

import           Control.Concurrent                    (threadDelay)
import           Control.Exception                     (SomeException, try)
import           Data.Conduit.Shell                    (grep, rm, run, shell,
                                                        ($|))
import           Data.Conduit.Shell.Segments           (strings)
import           Data.List                             (isInfixOf)
import           Data.Monoid                           ((<>))
import           System.FilePath.Posix                 ((</>))
import           System.Info                           (os)

import           Database.PostgreSQL.Embedded.Download
import           Database.PostgreSQL.Embedded.Types

startPostgres :: StartupConfig -> DBConfig -> IO RuntimeConfig
startPostgres (StartupConfig version_ t) dConfig@(DBConfig p u) = do
    e <- downloadPostgres getOS version_
    let d = e </> "data"
    run $ do
        shell $ e </> "bin" </> "initdb" <> " -A trust -U " <> u <> " -D " <> d <> " -E UTF-8"
        shell $ e </> "bin" </> "pg_ctl" <> " -D " <> d <> " -o \"-F -p " <>
            (show p) <> "\"" <> " -l " <> (e </> "log") <> " start"
    let r = RuntimeConfig e d
    checkPostgresStarted r dConfig t
    return $ r
    where
        getOS | "darwin" `isInfixOf` os = OSX
              | "linux" `isInfixOf` os = Linux
              | "win" `isInfixOf` os = Win
              | otherwise = error $ "Unsupported platform" <> os

stopPostgres :: RuntimeConfig -> IO ()
stopPostgres (RuntimeConfig e d) = run $ do
        shell $ e </> "bin" </> "pg_ctl" <> " -D " <> d <> " stop"
        rm "-rf" d

checkPostgresStarted :: RuntimeConfig -> DBConfig -> Int -> IO ()
checkPostgresStarted (RuntimeConfig e _) (DBConfig p _) secs = checkPostgresStarted_ secs >>= \_ -> return ()
    where
        checkPostgresStarted_ :: Int -> IO Bool
        checkPostgresStarted_ n = do
            let oneSec = 1000000
            let check = run $ shell (e </> "bin" </> "pg_isready" <> " -p " <> (show p) <> " -h localhost") >>= \_ -> return True

            res <- try check :: IO (Either SomeException Bool)
            let retry = if n > 0 then threadDelay oneSec >>=
                            \_ -> checkPostgresStarted_ (n - 1)
                        else
                            return False
            case res of
                Left err   -> print err >>= \_ -> retry
                Right res1 -> if (not res1) then retry else return True