content: -- This file is part of the Wire Server implementation.
  --
  -- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
  --
  -- This program is free software: you can redistribute it and/or modify it under
  -- the terms of the GNU Affero General Public License as published by the Free
  -- Software Foundation, either version 3 of the License, or (at your option) any
  -- later version.
  --
  -- This program is distributed in the hope that it will be useful, but WITHOUT
  -- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  -- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  -- details.
  --
  -- You should have received a copy of the GNU Affero General Public License along
  -- with this program. If not, see <https://www.gnu.org/licenses/>.
  
  module Gundeck.Push.Data
    ( insert,
      updateArn,
      delete,
      lookup,
      erase,
      Consistency (..),
    )
  where
  
  import Cassandra
  import Data.ByteString.Conversion
  import Data.Id (ClientId, ConnId, UserId)
  import Gundeck.Instances ()
  import Gundeck.Push.Native.Types
  import Imports hiding (lookup)
  import System.Logger.Class (MonadLogger, field, msg, val, (~~))
  import System.Logger.Class qualified as Log
  import Wire.API.Push.V2 hiding (token)
  
  lookup :: (MonadClient m, MonadLogger m) => UserId -> Consistency -> m [Address]
  lookup u c = foldM mk [] =<< retry x1 (query q (params c (Identity u)))
    where
      q :: PrepQuery R (Identity UserId) (UserId, Transport, AppName, Token, Maybe EndpointArn, ConnId, Maybe ClientId)
      q = "select usr, transport, app, ptoken, arn, connection, client from user_push where usr = ?"
      mk as r = maybe as (: as) <$> mkAddr r
  
  insert :: (MonadClient m) => UserId -> Transport -> AppName -> Token -> EndpointArn -> ConnId -> ClientId -> m ()
  insert u t a p e o c = retry x5 $ write q (params LocalQuorum (u, t, a, p, e, o, c))
    where
      q :: PrepQuery W (UserId, Transport, AppName, Token, EndpointArn, ConnId, ClientId) ()
      q = "insert into user_push (usr, transport, app, ptoken, arn, connection, client) values (?, ?, ?, ?, ?, ?, ?)"
  
  updateArn :: (MonadClient m) => UserId -> Transport -> AppName -> Token -> EndpointArn -> m ()
  updateArn uid transport app token arn = retry x5 $ write q (params LocalQuorum (arn, uid, transport, app, token))
    where
      q :: PrepQuery W (EndpointArn, UserId, Transport, AppName, Token) ()
      q = {- `IF EXISTS`, but that requires benchmarking -} "update user_push set arn = ? where usr = ? and transport = ? and app = ? and ptoken = ?"
  
  delete :: (MonadClient m) => UserId -> Transport -> AppName -> Token -> m ()
  delete u t a p = retry x5 $ write q (params LocalQuorum (u, t, a, p))
    where
      q :: PrepQuery W (UserId, Transport, AppName, Token) ()
      q = "delete from user_push where usr = ? and transport = ? and app = ? and ptoken = ?"
  
  erase :: (MonadClient m) => UserId -> m ()
  erase u = retry x5 $ write q (params LocalQuorum (Identity u))
    where
      q :: PrepQuery W (Identity UserId) ()
      q = "delete from user_push where usr = ?"
  
  mkAddr ::
    (MonadClient m, MonadLogger m) =>
    (UserId, Transport, AppName, Token, Maybe EndpointArn, ConnId, Maybe ClientId) ->
    m (Maybe Address)
  mkAddr (usr, trp, app, tok, arn, con, clt) = case (clt, arn) of
    (Just c, Just a) -> pure $! Just $! Address usr a con (pushToken trp app tok c)
    _ -> do
      Log.info $
        field "user" (toByteString usr)
          ~~ field "transport" (show trp)
          ~~ field "app" (appNameText app)
          ~~ field "token" (tokenText tok)
          ~~ msg (val "Deleting legacy push token without a client or ARN.")
      delete usr trp app tok
      pure Nothing
  