{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}

module Stack.Freeze
    ( freeze
    , FreezeOpts (..)
    , FreezeMode (..)
    ) where

import           Data.Aeson ((.=), object)
import qualified Data.Yaml as Yaml
import           RIO.Process
import qualified RIO.ByteString as B
import           Stack.Prelude
import           Stack.Types.Config

data FreezeMode = FreezeProject | FreezeSnapshot

newtype FreezeOpts = FreezeOpts
    { freezeMode :: FreezeMode
    }

freeze :: HasEnvConfig env => FreezeOpts -> RIO env ()
freeze (FreezeOpts mode) = do
  mproject <- view $ configL.to configProject
  let warn = logWarn "No project was found: nothing to freeze"
  case mproject of
    PCProject (p, _) -> doFreeze p mode
    PCGlobalProject -> warn
    PCNoProject _ -> warn

doFreeze ::
       (HasProcessContext env, HasLogFunc env, HasPantryConfig env)
    => Project
    -> FreezeMode
    -> RIO env ()
doFreeze p FreezeProject = do
  let deps = projectDependencies p
      resolver = projectResolver p
      completePackageLocation' pl =
        case pl of
          RPLImmutable pli -> PLImmutable <$> completePackageLocation pli
          RPLMutable m -> pure $ PLMutable m
  resolver' <- completeSnapshotLocation resolver
  deps' <- mapM completePackageLocation' deps
  let rawCompleted = map toRawPL deps'
      rawResolver = toRawSL resolver'
  if rawCompleted == deps && rawResolver == resolver
  then
    logInfo "No freezing is required for this project"
  else do
    logInfo "# Fields not mentioned below do not need to be updated"

    if rawResolver == resolver
      then logInfo "# No update to resolver is needed"
      else do
        logInfo "# Frozen version of resolver"
        B.putStr $ Yaml.encode $ object ["resolver" .= rawResolver]

    if rawCompleted == deps
      then logInfo "# No update to extra-deps is needed"
      else do
        logInfo "# Frozen version of extra-deps"
        B.putStr $ Yaml.encode $ object ["extra-deps" .= rawCompleted]

doFreeze p FreezeSnapshot = do
  resolver <- completeSnapshotLocation $ projectResolver p
  result <- loadSnapshotLayer resolver
  case result of
    Left _wc ->
      logInfo "No freezing is required for compiler resolver"
    Right snap -> do
      snap' <- completeSnapshotLayer snap
      let rawCompleted = toRawSnapshotLayer snap'
      if rawCompleted == snap
        then
        logInfo "No freezing is required for the snapshot of this project"
        else
        liftIO $ B.putStr $ Yaml.encode snap'