module Command.Publish (command) where

import Prelude

import Control.Monad.IO.Class (liftIO)
import Data.Aeson qualified as A
import Data.ByteString.Lazy.Char8 qualified as BL
import Data.Time.Clock (getCurrentTime)
import Data.Version (Version(..))
import Language.PureScript.Publish (PublishOptions(..), defaultPublishOptions, unsafePreparePackage, warn)
import Language.PureScript.Publish.ErrorsWarnings (PackageWarning(..))
import Options.Applicative (Parser)
import Options.Applicative qualified as Opts

data PublishOptionsCLI = PublishOptionsCLI
  { cliManifestPath :: FilePath
  , cliResolutionsPath :: FilePath
  , cliCompileOutputDir :: FilePath
  , cliDryRun :: Bool
  }

manifestPath :: Parser FilePath
manifestPath = Opts.strOption $
     Opts.long "manifest"
  <> Opts.metavar "FILE"
  <> Opts.help "The package manifest file"

resolutionsPath :: Parser FilePath
resolutionsPath = Opts.strOption $
     Opts.long "resolutions"
  <> Opts.metavar "FILE"
  <> Opts.help "The resolutions file"

dryRun :: Parser Bool
dryRun = Opts.switch $
     Opts.long "dry-run"
  <> Opts.help "Produce no output, and don't require a tagged version to be checked out."

compileOutputDir :: Opts.Parser FilePath
compileOutputDir = Opts.option Opts.auto $
     Opts.value "output"
  <> Opts.showDefault
  <> Opts.long "compile-output"
  <> Opts.metavar "DIR"
  <> Opts.help "Compiler output directory"

cliOptions :: Opts.Parser PublishOptionsCLI
cliOptions =
  PublishOptionsCLI <$> manifestPath <*> resolutionsPath <*> compileOutputDir <*> dryRun

mkPublishOptions :: PublishOptionsCLI -> PublishOptions
mkPublishOptions cliOpts =
  let
    opts =
      defaultPublishOptions
        { publishManifestFile = cliManifestPath cliOpts
        , publishResolutionsFile = cliResolutionsPath cliOpts
        , publishCompileOutputDir = cliCompileOutputDir cliOpts
        }
  in
    if cliDryRun cliOpts
      then
        opts
          { publishGetVersion = return ("0.0.0", Version [0,0,0] [])
          , publishGetTagTime = const (liftIO getCurrentTime)
          , publishWorkingTreeDirty = warn DirtyWorkingTreeWarn
          }
      else
        opts

command :: Opts.Parser (IO ())
command = publish <$> (Opts.helper <*> cliOptions)

publish :: PublishOptionsCLI -> IO ()
publish cliOpts = do
  let opts = mkPublishOptions cliOpts
  pkg <- unsafePreparePackage opts
  if cliDryRun cliOpts
    then putStrLn "Dry run completed, no errors."
    else BL.putStrLn (A.encode pkg)
