content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE ViewPatterns #-}
  {-# LANGUAGE LambdaCase #-}
  
  import UnliftIO
  import qualified Data.Aeson as Aeson
  import qualified Data.ByteString.Char8 as BS8
  import Data.Conduit.Binary as C
  import qualified DeckGo.Handler
  import qualified Network.AWS.Extended as AWS
  import qualified Network.HTTP.Types as HTTP
  import qualified Network.Wai as Wai
  import qualified Network.Wai.Handler.Lambda as Lambda
  import qualified Network.Wai.Middleware.Cors as Cors
  import qualified Hasql.Connection as Hasql
  import System.Environment (getEnv)
  import qualified Servant.Auth.Firebase as Firebase
  import qualified Data.Text as T
  import qualified Network.AWS.S3 as S3
  import Control.Lens
  import Control.Monad.Trans.Resource
  
  main :: IO ()
  main = do
      hSetBuffering stdin LineBuffering
      hSetBuffering stdout LineBuffering
  
      liftIO $ putStrLn "Booting..."
      env <- AWS.newEnv
  
      liftIO $ putStrLn "Booted!"
  
      settings <- getFirebaseSettings env
      conn <- getPostgresqlConnection
  
      putStrLn "Connection acquired, starting lambda..."
  
      Lambda.runSettings settings' $ cors $ DeckGo.Handler.application settings env conn
    where
      settings' = Lambda.defaultSettings
        { Lambda.timeoutValue = 20 * 1000 * 1000 }
  
  -- TODO: factor out
  getFirebaseSettings :: AWS.Env -> IO Firebase.FirebaseLoginSettings
  getFirebaseSettings env = do
      pid <- getEnv "FIREBASE_PROJECT_ID"
  
      metaBucketName <- getEnv "META_BUCKET_NAME"
      let metaBucket = S3.BucketName (T.pack metaBucketName)
      putStrLn $ "META Bucket is: " <> show metaBucket
  
      let fetchKeys = AWS.runResourceT $ do
            let okey = "google-public-keys"
  
            runAWS' env (
              AWS.send $ S3.getObject metaBucket okey
              ) >>= \case
                Right gors -> do
                  keysRaw <- (gors ^. S3.gorsBody) `AWS.sinkBody` C.sinkLbs
                  liftIO $ putStrLn $ "got new keys"
                  case Aeson.decode keysRaw of
                    Nothing -> error "Could not decode key file"
                    Just keyMap -> pure keyMap
                Left e -> error $ "Error in get: " <> show e
  
      pure Firebase.FirebaseLoginSettings
        { Firebase.firebaseLoginProjectId = Firebase.ProjectId (T.pack pid)
        , Firebase.firebaseLoginGetKeys = fetchKeys
        }
  
  getPostgresqlConnection :: IO Hasql.Connection
  getPostgresqlConnection = do
      putStrLn "Reading connection info..."
      user <- getEnv "PGUSER"
      password <- getEnv "PGPASSWORD"
      host <- getEnv "PGHOST"
      db <- getEnv "PGDATABASE"
      port <- getEnv "PGPORT"
      putStrLn "Acquiring connection..."
      Hasql.acquire (
        Hasql.settings
          (BS8.pack host)
          (read port)
          (BS8.pack user)
          (BS8.pack password)
          (BS8.pack db)
        ) >>= \case
          Left e -> error (show e)
          Right c -> pure c
  
  cors :: Wai.Middleware
  cors = Cors.cors $
        const $
        Just Cors.simpleCorsResourcePolicy { Cors.corsMethods = methods }
  
  methods :: [HTTP.Method]
  methods =
      [ "GET"
      , "HEAD"
      , "POST"
      , "DELETE"
      , "PUT"
      ]
  
  runAWS'
    :: (MonadResource m, MonadIO m, MonadUnliftIO m)
    => AWS.Env -> AWS.AWS a -> m (Either SomeException a)
  runAWS' env =
      tryAny .
      AWS.runAWS env .
      AWS.within AWS.NorthVirginia
  