-- SPDX-FileCopyrightText: 2022 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

-- | Timestamp range utility functions
module Hedgehog.Range.Tezos.Core.Timestamp
  ( minTimestamp
  , maxTimestamp
  , midTimestamp
  , timestampRangeFromSeconds
  ) where

import Data.Time.Calendar (Day, addDays, diffDays)
import Data.Time.Clock (UTCTime(..))
import Data.Time.Format (defaultTimeLocale, parseTimeM)
import Hedgehog.Range (Range)
import Hedgehog.Range qualified as Range

import Morley.Tezos.Core (Timestamp, timestampFromSeconds, timestampFromUTCTime)

-- | Generate a linear 'Range' of timestamps between lower and upper bounds specified in seconds
-- from the start of the UNIX epoch (Jan 1st 1970).
timestampRangeFromSeconds :: Integer -> Integer -> Range Timestamp
timestampRangeFromSeconds :: Integer -> Integer -> Range Timestamp
timestampRangeFromSeconds Integer
l Integer
h = Integer -> Timestamp
timestampFromSeconds (Integer -> Timestamp) -> Range Integer -> Range Timestamp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Integer -> Integer -> Range Integer
forall a. Integral a => a -> a -> Range a
Range.linear Integer
l Integer
h

-- | Minimal (earliest) timestamp used for the default @Range Timestamp@
minTimestamp :: Timestamp
minTimestamp :: Timestamp
minTimestamp = UTCTime -> Timestamp
timestampFromUTCTime (UTCTime -> Timestamp) -> UTCTime -> Timestamp
forall a b. (a -> b) -> a -> b
$ Day -> DiffTime -> UTCTime
UTCTime Day
minDay (Integer -> DiffTime
forall a b.
(Integral a, RealFrac b, CheckIntSubType a Integer) =>
a -> b
fromIntegralToRealFrac Integer
minSec)

-- | Maximal (latest) timestamp used for the default @Range Timestamp@
maxTimestamp :: Timestamp
maxTimestamp :: Timestamp
maxTimestamp = UTCTime -> Timestamp
timestampFromUTCTime (UTCTime -> Timestamp) -> UTCTime -> Timestamp
forall a b. (a -> b) -> a -> b
$ Day -> DiffTime -> UTCTime
UTCTime Day
maxDay (Integer -> DiffTime
forall a b.
(Integral a, RealFrac b, CheckIntSubType a Integer) =>
a -> b
fromIntegralToRealFrac Integer
maxSec)

-- | Median of 'minTimestamp' and 'maxTimestamp'.
-- Useful for testing (exactly half of generated dates will be before and after
-- this date).
midTimestamp :: Timestamp
midTimestamp :: Timestamp
midTimestamp = UTCTime -> Timestamp
timestampFromUTCTime (UTCTime -> Timestamp) -> UTCTime -> Timestamp
forall a b. (a -> b) -> a -> b
$
  Day -> DiffTime -> UTCTime
UTCTime ( ((Day
maxDay Day -> Day -> Integer
`diffDays` Day
minDay) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
2) Integer -> Day -> Day
`addDays` Day
minDay)
          (Integer -> DiffTime
forall a b.
(Integral a, RealFrac b, CheckIntSubType a Integer) =>
a -> b
fromIntegralToRealFrac (Integer -> DiffTime) -> Integer -> DiffTime
forall a b. (a -> b) -> a -> b
$ (Integer
maxSec Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
minSec) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
2)

minDay :: Day
minDay :: Day
minDay = Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe (Text -> Day
forall a. HasCallStack => Text -> a
error Text
"failed to parse day 2008-11-01") (Maybe Day -> Day) -> Maybe Day -> Day
forall a b. (a -> b) -> a -> b
$
            Bool -> TimeLocale -> String -> String -> Maybe Day
forall (m :: * -> *) t.
(MonadFail m, ParseTime t) =>
Bool -> TimeLocale -> String -> String -> m t
parseTimeM Bool
True TimeLocale
defaultTimeLocale String
"%Y-%-m-%-d" String
"2008-11-01"

maxDay :: Day
maxDay :: Day
maxDay = Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe (Text -> Day
forall a. HasCallStack => Text -> a
error Text
"failed to parse day 2024-11-01") (Maybe Day -> Day) -> Maybe Day -> Day
forall a b. (a -> b) -> a -> b
$
            Bool -> TimeLocale -> String -> String -> Maybe Day
forall (m :: * -> *) t.
(MonadFail m, ParseTime t) =>
Bool -> TimeLocale -> String -> String -> m t
parseTimeM Bool
True TimeLocale
defaultTimeLocale String
"%Y-%-m-%-d" String
"2024-11-01"

minSec :: Integer
minSec :: Integer
minSec = Integer
0

maxSec :: Integer
maxSec :: Integer
maxSec = Integer
86399