module B9.B9Config.SystemdNspawn
  ( systemdNspawnConfigToCPDocument,
    defaultSystemdNspawnConfig,
    parseSystemdNspawnConfig,
    SystemdNspawnConfig (..),
    SystemdNspawnConsole (..),
    systemdNspawnCapabilities,
    systemdNspawnUseSudo,
    systemdNspawnMaxLifetimeSeconds,
    systemdNspawnExtraArgs,
    systemdNspawnExecutable,
    systemdNspawnConsole,
  )
where

import B9.B9Config.Container
import Control.Lens (makeLenses)
import Data.ConfigFile.B9Extras
import qualified Text.ParserCombinators.ReadP as ReadP
import qualified Text.ParserCombinators.ReadPrec as ReadPrec
import Text.Read
import Test.QuickCheck (Arbitrary(arbitrary))
import qualified Test.QuickCheck as QuickCheck
import B9.QCUtil (smaller, arbitraryFilePath)

-- TODO document b9 config file
data SystemdNspawnConfig
  = SystemdNspawnConfig
      { _systemdNspawnCapabilities :: [ContainerCapability],
        _systemdNspawnUseSudo :: Bool,
        _systemdNspawnMaxLifetimeSeconds :: Maybe Int,
        _systemdNspawnExtraArgs :: Maybe String,
        _systemdNspawnExecutable :: Maybe FilePath,
        _systemdNspawnConsole :: SystemdNspawnConsole
      }
  deriving (Read, Show, Eq)

instance Arbitrary SystemdNspawnConfig where
  arbitrary =
    SystemdNspawnConfig
    <$> smaller arbitrary
    <*> smaller arbitrary
    <*> smaller arbitrary
    <*> smaller arbitrary
    <*> smaller (QuickCheck.oneof [pure Nothing, Just <$> arbitraryFilePath])
    <*> smaller arbitrary

data SystemdNspawnConsole
  = SystemdNspawnInteractive
  | SystemdNspawnReadOnly
  | SystemdNspawnPassive
  | SystemdNspawnPipe
  deriving (Eq)

instance Arbitrary SystemdNspawnConsole where
  arbitrary =
    QuickCheck.elements
      [ SystemdNspawnInteractive
      , SystemdNspawnReadOnly
      , SystemdNspawnPassive
      , SystemdNspawnPipe
      ]

instance Show SystemdNspawnConsole where
  show x = case x of
    SystemdNspawnInteractive -> "interactive"
    SystemdNspawnReadOnly -> "read-only"
    SystemdNspawnPassive -> "passive"
    SystemdNspawnPipe -> "pipe"

instance Read SystemdNspawnConsole where
  readPrec =
    do
      Ident "interactive" <- lexP
      return SystemdNspawnInteractive
      +++ ReadPrec.lift
        ( do
            ReadP.skipSpaces
            _ <- ReadP.string "read-only"
            return SystemdNspawnReadOnly
        )
      +++ do
        Ident "passive" <- lexP
        return SystemdNspawnPassive
      +++ do
        Ident "pipe" <- lexP
        return SystemdNspawnPipe

makeLenses ''SystemdNspawnConfig

defaultSystemdNspawnConfig :: SystemdNspawnConfig
defaultSystemdNspawnConfig =
  SystemdNspawnConfig
    [ CAP_MKNOD,
      CAP_SYS_ADMIN,
      CAP_SYS_CHROOT,
      CAP_SETGID,
      CAP_SETUID,
      CAP_NET_BIND_SERVICE,
      CAP_SETPCAP,
      CAP_SYS_PTRACE,
      CAP_SYS_MODULE
    ]
    True
    (Just (4 * 3600))
    Nothing
    Nothing
    SystemdNspawnReadOnly

cfgFileSection :: String
cfgFileSection = "systemdNspawn"

useSudoK :: String
useSudoK = "use_sudo"

maxLifetimeSecondsK :: String
maxLifetimeSecondsK = "max_lifetime_seconds"

extraArgsK :: String
extraArgsK = "extra_args"

executableK :: String
executableK = "executable"

consoleK :: String
consoleK = "console"

systemdNspawnConfigToCPDocument ::
  SystemdNspawnConfig -> CPDocument -> Either CPError CPDocument
systemdNspawnConfigToCPDocument c cp = do
  cp1 <- addSectionCP cp cfgFileSection
  cp2 <-
    containerCapsToCPDocument cp1 cfgFileSection $
      _systemdNspawnCapabilities c
  cp3 <- setShowCP cp2 cfgFileSection useSudoK $ _systemdNspawnUseSudo c
  cp4 <- setShowCP cp3 cfgFileSection maxLifetimeSecondsK $ _systemdNspawnMaxLifetimeSeconds c
  cp5 <- setShowCP cp4 cfgFileSection extraArgsK $ _systemdNspawnExtraArgs c
  cp6 <- setShowCP cp5 cfgFileSection executableK $ _systemdNspawnExecutable c
  setShowCP cp6 cfgFileSection consoleK $ _systemdNspawnConsole c

parseSystemdNspawnConfig :: CPDocument -> Either CPError SystemdNspawnConfig
parseSystemdNspawnConfig cp =
  let getr :: (CPGet a) => CPOptionSpec -> Either CPError a
      getr = readCP cp cfgFileSection
   in SystemdNspawnConfig
        <$> parseContainerCapabilities cp cfgFileSection
        <*> getr useSudoK
        <*> getr maxLifetimeSecondsK
        <*> getr extraArgsK
        <*> getr executableK
        <*> getr consoleK