content: {-# LANGUAGE OverloadedLists #-}
  {-# LANGUAGE PatternSynonyms #-}
  {-# LANGUAGE TemplateHaskell #-}
  
  module Simplex.Chat.Remote.AppVersion
    ( AppVersionRange (minVersion, maxVersion),
      pattern AppVersionRange,
      AppVersion (..),
      pattern AppCompatible,
      mkAppVersionRange,
      compatibleAppVersion,
      isAppCompatible,
    )
  where
  
  import Data.Aeson (FromJSON (..), ToJSON (..))
  import qualified Data.Aeson as J
  import qualified Data.Aeson.Encoding as JE
  import qualified Data.Aeson.TH as JQ
  import qualified Data.Text as T
  import Data.Version (parseVersion, showVersion)
  import qualified Data.Version as V
  import Simplex.Messaging.Parsers (defaultJSON)
  import Text.ParserCombinators.ReadP (readP_to_S)
  
  newtype AppVersion = AppVersion {appVersion :: V.Version}
    deriving (Eq, Ord, Show)
  
  instance ToJSON AppVersion where
    toJSON (AppVersion v) = J.String . T.pack $ showVersion v
    toEncoding (AppVersion v) = JE.text . T.pack $ showVersion v
  
  instance FromJSON AppVersion where
    parseJSON = J.withText "AppVersion" $ parse . T.unpack
      where
        parse s = case filter (null . snd) $ readP_to_S parseVersion s of
          (v, _) : _ -> pure $ AppVersion v
          _ -> fail $ "bad AppVersion: " <> s
  
  data AppVersionRange = AppVRange
    { minVersion :: AppVersion,
      maxVersion :: AppVersion
    }
    deriving (Show)
  
  pattern AppVersionRange :: AppVersion -> AppVersion -> AppVersionRange
  pattern AppVersionRange v1 v2 <- AppVRange v1 v2
  
  {-# COMPLETE AppVersionRange #-}
  
  mkAppVersionRange :: AppVersion -> AppVersion -> AppVersionRange
  mkAppVersionRange v1 v2
    | v1 <= v2 = AppVRange v1 v2
    | otherwise = error "invalid version range"
  
  newtype AppCompatible a = AppCompatible_ a
  
  pattern AppCompatible :: a -> AppCompatible a
  pattern AppCompatible a <- AppCompatible_ a
  
  {-# COMPLETE AppCompatible #-}
  
  isAppCompatible :: AppVersion -> AppVersionRange -> Bool
  isAppCompatible v (AppVRange v1 v2) = v1 <= v && v <= v2
  
  isCompatibleAppRange :: AppVersionRange -> AppVersionRange -> Bool
  isCompatibleAppRange (AppVRange min1 max1) (AppVRange min2 max2) = min1 <= max2 && min2 <= max1
  
  compatibleAppVersion :: AppVersionRange -> AppVersionRange -> Maybe (AppCompatible AppVersion)
  compatibleAppVersion vr1 vr2 =
    min (maxVersion vr1) (maxVersion vr2) `mkCompatibleIf` isCompatibleAppRange vr1 vr2
  
  mkCompatibleIf :: AppVersion -> Bool -> Maybe (AppCompatible AppVersion)
  v `mkCompatibleIf` cond = if cond then Just $ AppCompatible_ v else Nothing
  
  $(JQ.deriveJSON defaultJSON ''AppVersionRange)
  