module Chiasma.Test.Tmux(
  withTestTmux,
  tmuxSpec,
) where

import GHC.Real (fromIntegral)
import GHC.IO.Handle (Handle)
import System.FilePath ((</>))
import System.Posix.Pty (Pty, resizePty, createPty)
import System.Posix.Terminal (openPseudoTerminal)
import System.Posix.IO (fdToHandle)
import qualified System.Posix.Signals as Signal (signalProcess, killProcess)
import System.Process (getPid)
import System.Process.Typed (
  ProcessConfig,
  Process,
  withProcess,
  proc,
  setStdin,
  setStdout,
  setStderr,
  useHandleClose,
  unsafeProcessHandle,
  )
import UnliftIO (finally)
import UnliftIO.Temporary (withSystemTempDirectory)
import Chiasma.Native.Api (TmuxNative(..))
import Chiasma.Test.File (fixture)

data Terminal = Terminal Handle Pty

unsafeTerminal :: IO Terminal
unsafeTerminal = do
  (_, slave) <- openPseudoTerminal
  mayPty <- createPty slave
  handle <- fdToHandle slave
  pty <- maybe (error "couldn't spawn pty") return mayPty
  return $ Terminal handle pty

testTmuxProcessConfig :: FilePath -> FilePath -> Terminal -> IO (ProcessConfig () () ())
testTmuxProcessConfig socket confFile (Terminal handle pty) = do
  resizePty pty (1000, 1000)
  let stream = useHandleClose handle
  let stdio = setStdin stream . setStdout stream . setStderr stream
  return $ stdio $ proc "tmux" ["-S", socket, "-f", confFile]

killPid :: Integral a => a -> IO ()
killPid =
  Signal.signalProcess Signal.killProcess . fromIntegral

killProcess :: Process () () () -> IO ()
killProcess prc = do
  let handle = unsafeProcessHandle prc
  mayPid <- getPid handle
  maybe (return ()) killPid mayPid

runAndKillTmux :: (TmuxNative -> IO a) -> TmuxNative -> Process () () () -> IO a
runAndKillTmux thunk api prc =
  finally (thunk api) (killProcess prc)

withTestTmux :: (TmuxNative -> IO a) -> FilePath -> IO a
withTestTmux thunk tempDir = do
  let socket = tempDir </> "tmux_socket"
  conf <- fixture "u" "tmux.conf"
  terminal <- unsafeTerminal
  pc <- testTmuxProcessConfig socket conf terminal
  withProcess pc $ runAndKillTmux thunk (TmuxNative socket)

tmuxSpec :: (TmuxNative -> IO a) -> IO a
tmuxSpec thunk =
  withSystemTempDirectory "chiasma-test" $ withTestTmux thunk