module B9.Shake.SharedImageRulesSpec
  ( spec,
  )
where

import B9.Artifact.Readable
import B9.Artifact.Readable.Interpreter (assemble)
import B9.B9Config
import B9.B9Monad
import B9.DiskImages
import B9.Repository
import B9.RepositoryIO
import B9.Shake.SharedImageRules
import B9.Vm
import Control.Exception
import Control.Monad
import qualified Data.Set as Set
import Development.Shake as Shake
import System.Directory
import System.Environment
import System.FilePath
import System.IO.B9Extras
import Test.Hspec
import Text.Printf

testShakeBuild :: B9ConfigOverride -> IO ()
testShakeBuild cfg = shake shakeOptions $ do
  enableSharedImageRules cfg
  customSharedImageAction (SharedImageName "test") $
    liftIO
      ( b9Build
          cfg
          ( void
              ( assemble
                  ( Artifact
                      (IID "test-image")
                      ( VmImages
                          [ ImageTarget
                              (Share "test" Raw KeepSize)
                              (EmptyImage "test" Ext4 Raw (ImageSize 10 MB))
                              NotMounted
                          ]
                          NoVmScript
                      )
                  )
              )
          )
      )
  action (needSharedImage (SharedImageName "test"))

spec :: HasCallStack => Spec
spec = do
  context "missing shared image" $ do
    it "builds a missing image" $ do
      withTempBuildDirs $ \cfg -> do
        testShakeBuild cfg
        actualImages <- b9Build cfg (allCachedSharedImages <$> getSharedImages)
        Set.size actualImages `shouldBe` 1

b9Build :: HasCallStack => B9ConfigOverride -> B9 a -> IO a
b9Build cfg effect =
  runB9ConfigActionWithOverrides
    (runB9 effect)
    cfg

withTempBuildDirs :: HasCallStack => (B9ConfigOverride -> IO a) -> IO a
withTempBuildDirs k =
  bracket acquire release use
  where
    acquire = do
      nixOutDirEnv <- lookupEnv "NIX_BUILD_TOP"
      let rootDir = maybe InTempDir (((.) . (.)) Path (</>)) nixOutDirEnv
      repoRelPath <- printf "testsRepositoryIOSpec-test-repo-%U" <$> randomUUID
      buildRelPath <- printf "RepositoryIOSpec-root-%U" <$> randomUUID
      cfgRelPath <- printf "RepositoryIOSpec-b9cfg-%U" <$> randomUUID
      let tmpRepoPath = rootDir ("tests" </> repoRelPath)
          tmpBuildPath = rootDir ("tests" </> buildRelPath)
          tmpCfgPath = rootDir ("tests" </> cfgRelPath)
      ensureSystemPath tmpRepoPath
      ensureSystemPath tmpBuildPath
      tmpBuildPathFileName <- resolve tmpBuildPath
      return (tmpRepoPath, tmpBuildPathFileName, tmpCfgPath)
    release (tmpRepoPath, tmpBuildPathFileName, tmpCfgPath) = do
      let cleanupTmpPath = removePathForcibly <=< resolve
      cleanupTmpPath tmpRepoPath
      cleanupTmpPath tmpCfgPath
      removePathForcibly tmpBuildPathFileName
    use (tmpRepoPath, tmpBuildPathFileName, tmpCfgPath) =
      let mkCfg cfgIn =
            cfgIn
              { _repositoryCache = Just tmpRepoPath,
                _projectRoot = Just tmpBuildPathFileName
              }
          oCfg =
            overrideB9Config
              mkCfg
              ( overrideWorkingDirectory
                  tmpBuildPathFileName
                  ( overrideDefaultB9ConfigPath
                      tmpCfgPath
                      noB9ConfigOverride
                  )
              )
       in k oCfg
