{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ViewPatterns      #-}

module Pantry.Tree
  ( unpackTree
  , rawParseGPD
  ) where

import           Distribution.PackageDescription ( GenericPackageDescription )
import           Distribution.PackageDescription.Parsec
import           Distribution.Parsec ( PWarning (..) )
import           Pantry.Storage hiding
                   ( Tree, TreeEntry, findOrGenerateCabalFile )
import           Pantry.Types
import           Path ( Abs, Dir, File, Path, toFilePath )
import           RIO
import qualified RIO.ByteString as B
import           RIO.Directory
                   ( createDirectoryIfMissing, getPermissions
                   , setOwnerExecutable, setPermissions
                   )
import           RIO.FilePath ((</>), takeDirectory)
import qualified RIO.Map as Map
import qualified RIO.Text as T

unpackTree ::
     (HasPantryConfig env, HasLogFunc env)
  => RawPackageLocationImmutable -- for exceptions
  -> Path Abs Dir -- ^ dest dir, will be created if necessary
  -> Tree
  -> RIO env ()
unpackTree rpli (toFilePath -> dir) (TreeMap m) = do
  withStorage $ for_ (Map.toList m) $ \(sfp, TreeEntry blobKey ft) -> do
    let dest = dir </> T.unpack (unSafeFilePath sfp)
    createDirectoryIfMissing True $ takeDirectory dest
    mbs <- loadBlob blobKey
    case mbs of
      Nothing -> do
        -- TODO when we have pantry wire stuff, try downloading
        throwIO $ TreeReferencesMissingBlob rpli sfp blobKey
      Just bs -> do
        B.writeFile dest bs
        case ft of
          FTNormal -> pure ()
          FTExecutable -> liftIO $ do
            perms <- getPermissions dest
            setPermissions dest $ setOwnerExecutable True perms

-- | A helper function that performs the basic character encoding necessary.
rawParseGPD ::
     MonadThrow m
  => Either RawPackageLocationImmutable (Path Abs File)
  -> ByteString
  -> m ([PWarning], GenericPackageDescription)
rawParseGPD loc bs =
  case eres of
    Left (mversion, errs) ->
      throwM $ InvalidCabalFile loc mversion (toList errs) warnings
    Right gpkg -> pure (warnings, gpkg)
 where
  (warnings, eres) = runParseResult $ parseGenericPackageDescription bs
