-- SPDX-FileCopyrightText: 2026 Alexandra de Wit
--
-- SPDX-License-Identifier: MIT

{- | The receipt-lease vocabulary a queue backend writes and the worker's renewal controller
reads ("Ecluse.Core.Worker.Lease").

The monotonic instants come from "Ecluse.Core.Clock" and are re-exported here, because a lease
is where most callers meet them. A backend that never expires a delivery supplies no lease.
-}
module Ecluse.Core.Queue.Lease (
    -- * Durations
    Seconds (..),

    -- * The clock a lease is measured on
    MonoTime (..),
    monotonicNow,
    monoAfter,
    monoSecondsBetween,

    -- * One delivery's lease
    ReceiptLease (..),
    receiptLease,
) where

import Ecluse.Core.Clock (MonoTime (MonoTime), monoAfter, monoSecondsBetween, monotonicNow)

{- | A duration in whole seconds, for 'Ecluse.Core.Queue.extendVisibility'. A 'newtype', so a
raw @Int@ of seconds cannot pass for some other count.
-}
newtype Seconds = Seconds Int
    deriving stock (Seconds -> Seconds -> Bool
(Seconds -> Seconds -> Bool)
-> (Seconds -> Seconds -> Bool) -> Eq Seconds
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Seconds -> Seconds -> Bool
== :: Seconds -> Seconds -> Bool
$c/= :: Seconds -> Seconds -> Bool
/= :: Seconds -> Seconds -> Bool
Eq, Eq Seconds
Eq Seconds =>
(Seconds -> Seconds -> Ordering)
-> (Seconds -> Seconds -> Bool)
-> (Seconds -> Seconds -> Bool)
-> (Seconds -> Seconds -> Bool)
-> (Seconds -> Seconds -> Bool)
-> (Seconds -> Seconds -> Seconds)
-> (Seconds -> Seconds -> Seconds)
-> Ord Seconds
Seconds -> Seconds -> Bool
Seconds -> Seconds -> Ordering
Seconds -> Seconds -> Seconds
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Seconds -> Seconds -> Ordering
compare :: Seconds -> Seconds -> Ordering
$c< :: Seconds -> Seconds -> Bool
< :: Seconds -> Seconds -> Bool
$c<= :: Seconds -> Seconds -> Bool
<= :: Seconds -> Seconds -> Bool
$c> :: Seconds -> Seconds -> Bool
> :: Seconds -> Seconds -> Bool
$c>= :: Seconds -> Seconds -> Bool
>= :: Seconds -> Seconds -> Bool
$cmax :: Seconds -> Seconds -> Seconds
max :: Seconds -> Seconds -> Seconds
$cmin :: Seconds -> Seconds -> Seconds
min :: Seconds -> Seconds -> Seconds
Ord, Int -> Seconds -> ShowS
[Seconds] -> ShowS
Seconds -> String
(Int -> Seconds -> ShowS)
-> (Seconds -> String) -> ([Seconds] -> ShowS) -> Show Seconds
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Seconds -> ShowS
showsPrec :: Int -> Seconds -> ShowS
$cshow :: Seconds -> String
show :: Seconds -> String
$cshowList :: [Seconds] -> ShowS
showList :: [Seconds] -> ShowS
Show)

{- | What one delivery's lease grants: the window it stays hidden for, when that window
lapses, and the ceiling the backend holds the whole receipt under.
-}
data ReceiptLease = ReceiptLease
    { ReceiptLease -> Seconds
rlWindow :: Seconds
    -- ^ The window the backend granted, which each renewal asks for again.
    , ReceiptLease -> MonoTime
rlExpiresAt :: MonoTime
    -- ^ When the current window lapses and another consumer may take the delivery.
    , ReceiptLease -> MonoTime
rlCeilingAt :: MonoTime
    {- ^ The last instant the backend holds this receipt at all, however often it is renewed
    (SQS allows twelve hours from the first receipt).
    -}
    }
    deriving stock (ReceiptLease -> ReceiptLease -> Bool
(ReceiptLease -> ReceiptLease -> Bool)
-> (ReceiptLease -> ReceiptLease -> Bool) -> Eq ReceiptLease
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ReceiptLease -> ReceiptLease -> Bool
== :: ReceiptLease -> ReceiptLease -> Bool
$c/= :: ReceiptLease -> ReceiptLease -> Bool
/= :: ReceiptLease -> ReceiptLease -> Bool
Eq, Int -> ReceiptLease -> ShowS
[ReceiptLease] -> ShowS
ReceiptLease -> String
(Int -> ReceiptLease -> ShowS)
-> (ReceiptLease -> String)
-> ([ReceiptLease] -> ShowS)
-> Show ReceiptLease
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ReceiptLease -> ShowS
showsPrec :: Int -> ReceiptLease -> ShowS
$cshow :: ReceiptLease -> String
show :: ReceiptLease -> String
$cshowList :: [ReceiptLease] -> ShowS
showList :: [ReceiptLease] -> ShowS
Show)

{- | A lease stamped from the instant the delivery was asked for, so it never claims more
time than the backend granted.
-}
receiptLease :: MonoTime -> Seconds -> Seconds -> ReceiptLease
receiptLease :: MonoTime -> Seconds -> Seconds -> ReceiptLease
receiptLease MonoTime
receivedAt Seconds
window Seconds
maxHold =
    ReceiptLease
        { rlWindow :: Seconds
rlWindow = Seconds
window
        , rlExpiresAt :: MonoTime
rlExpiresAt = MonoTime -> Double -> MonoTime
monoAfter MonoTime
receivedAt (Seconds -> Double
secondsOf Seconds
window)
        , rlCeilingAt :: MonoTime
rlCeilingAt = MonoTime -> Double -> MonoTime
monoAfter MonoTime
receivedAt (Seconds -> Double
secondsOf Seconds
maxHold)
        }

secondsOf :: Seconds -> Double
secondsOf :: Seconds -> Double
secondsOf (Seconds Int
seconds) = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
seconds