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

-- 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)

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

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 SystemdNspawnReadOnly

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