-- 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/>.
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}

{-# HLINT ignore "Use camelCase" #-}

-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2025 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 Main
  ( main,
  )
where

import Amazonka (Region (Ireland))
import Control.Lens ((^.))
import Criterion.Main
import Data.Id (ClientId (..), ConnId (..), randomId)
import Data.Text.Lazy qualified as LT
import Data.UUID.V4 (nextRandom)
import Gundeck.Options
import Gundeck.Push.Native.Serialise
import Gundeck.Push.Native.Types
import Gundeck.ThreadBudget.Internal
import Imports
import OpenSSL (withOpenSSL)
import System.Random (randomRIO)
import Wire.API.Push.V2

main :: IO ()
main = withOpenSSL $ do
  prepared <- prepareBudgetState 100000
  defaultMain
    [ bgroup
        "notice"
        [ bench "32" $ nfIO notice
        ],
      bgroup
        "ThreadBudget"
        [ bench "budgetSpent'" $ nfIO (bench_BudgetSpent' prepared),
          bench "prepare + budgetSpent'" $ nfIO (bench_BudgetSpent' =<< prepareBudgetState 100000)
        ]
    ]

-----------------------------------------------------------------------------
-- Benchmarks

notice :: IO Text
notice = do
  i <- randomId
  a <- mkAddress GCM
  let msg = NativePush i HighPriority Nothing
      uid = a ^. addrUser
      transp = a ^. addrTransport
  Right txt <- pure $ serialise msg uid transp
  pure $! LT.toStrict txt

bench_BudgetSpent' :: IORef BudgetMap -> IO ()
bench_BudgetSpent' ref = do
  budgetmap <- readIORef ref
  void $ pure $ budgetSpent' budgetmap

-----------------------------------------------------------------------------
-- Utilities

mkAddress :: Transport -> IO Address
mkAddress t = do
  u <- randomId
  let app = AppName "test"
  let ept = mkEndpoint t app
  let tok = Token "test"
  let con = ConnId "conn"
  let clt = ClientId 3265102391
  pure $! Address u ept con (pushToken t app tok clt)

mkEndpoint :: Transport -> AppName -> EndpointArn
mkEndpoint t a = mkSnsArn Ireland (Account "test") topic
  where
    topic = mkEndpointTopic (ArnEnv "test") t a (EndpointId "test")

prepareBudgetState :: Int -> IO (IORef BudgetMap)
prepareBudgetState size = do
  ref <- _threadBudgetRunning <$> mkThreadBudgetState (MaxConcurrentNativePushes Nothing Nothing)
  forM_ [1 .. size] $ \_ -> do
    key <- nextRandom
    weight <- randomRIO (1, 1000)
    allocate ref key weight
  pure ref
