module Stackctl.Spec.Generate
  ( Generate (..)
  , GenerateSpec (..)
  , GenerateTemplate (..)
  , generate
  , TemplateFormat (..)
  ) where

import Stackctl.Prelude

import Stackctl.AWS
import Stackctl.AWS.Scope
import Stackctl.Action
import Stackctl.Config (HasConfig)
import Stackctl.DirectoryOption
import Stackctl.Spec.Discover (buildSpecPath)
import Stackctl.StackSpec
import Stackctl.StackSpecPath
import Stackctl.StackSpecYaml

data Generate = Generate
  { Generate -> Maybe StackDescription
gDescription :: Maybe StackDescription
  , Generate -> Maybe [StackName]
gDepends :: Maybe [StackName]
  , Generate -> Maybe [Action]
gActions :: Maybe [Action]
  , Generate -> Maybe [Parameter]
gParameters :: Maybe [Parameter]
  , Generate -> Maybe [Capability]
gCapabilities :: Maybe [Capability]
  , Generate -> Maybe [Tag]
gTags :: Maybe [Tag]
  , Generate -> GenerateSpec
gSpec :: GenerateSpec
  , Generate -> GenerateTemplate
gTemplate :: GenerateTemplate
  , Generate -> Bool
gOverwrite :: Bool
  }

data GenerateSpec
  = -- | Generate at an inferred name
    GenerateSpec StackName
  | -- | Generate to a given path
    GenerateSpecTo StackName FilePath

data GenerateTemplate
  = -- | Generate at an inferred name
    GenerateTemplate TemplateBody TemplateFormat
  | -- | Generate to the given path
    GenerateTemplateTo TemplateBody FilePath
  | -- | Assume template exists
    UseExistingTemplate FilePath

data TemplateFormat
  = TemplateFormatYaml
  | TemplateFormatJson

generate
  :: ( MonadMask m
     , MonadUnliftIO m
     , MonadLogger m
     , MonadReader env m
     , HasConfig env
     , HasAwsScope env
     , HasDirectoryOption env
     )
  => Generate
  -> m FilePath
generate :: forall (m :: * -> *) env.
(MonadMask m, MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasConfig env, HasAwsScope env, HasDirectoryOption env) =>
Generate -> m FilePath
generate Generate {Bool
Maybe [Tag]
Maybe [Parameter]
Maybe [Capability]
Maybe [StackName]
Maybe [Action]
Maybe StackDescription
GenerateTemplate
GenerateSpec
gOverwrite :: Bool
gTemplate :: GenerateTemplate
gSpec :: GenerateSpec
gTags :: Maybe [Tag]
gCapabilities :: Maybe [Capability]
gParameters :: Maybe [Parameter]
gActions :: Maybe [Action]
gDepends :: Maybe [StackName]
gDescription :: Maybe StackDescription
gOverwrite :: Generate -> Bool
gTemplate :: Generate -> GenerateTemplate
gSpec :: Generate -> GenerateSpec
gTags :: Generate -> Maybe [Tag]
gCapabilities :: Generate -> Maybe [Capability]
gParameters :: Generate -> Maybe [Parameter]
gActions :: Generate -> Maybe [Action]
gDepends :: Generate -> Maybe [StackName]
gDescription :: Generate -> Maybe StackDescription
..} = do
  let
    (StackName
stackName, FilePath
stackPath) = case GenerateSpec
gSpec of
      GenerateSpec StackName
name -> (StackName
name, Text -> FilePath
unpack (StackName -> Text
unStackName StackName
name) forall a. Semigroup a => a -> a -> a
<> FilePath
".yaml")
      GenerateSpecTo StackName
name FilePath
path -> (StackName
name, FilePath
path)

    (Maybe TemplateBody
mTemplateBody, FilePath
templatePath) = case GenerateTemplate
gTemplate of
      GenerateTemplate TemplateBody
body TemplateFormat
format ->
        ( forall a. a -> Maybe a
Just TemplateBody
body
        , case TemplateFormat
format of
            TemplateFormat
TemplateFormatYaml -> Text -> FilePath
unpack (StackName -> Text
unStackName StackName
stackName) forall a. Semigroup a => a -> a -> a
<> FilePath
".yaml"
            TemplateFormat
TemplateFormatJson -> Text -> FilePath
unpack (StackName -> Text
unStackName StackName
stackName) forall a. Semigroup a => a -> a -> a
<> FilePath
".json"
        )
      GenerateTemplateTo TemplateBody
body FilePath
path -> (forall a. a -> Maybe a
Just TemplateBody
body, FilePath
path)
      UseExistingTemplate FilePath
path -> (forall a. Maybe a
Nothing, FilePath
path)

    specYaml :: StackSpecYaml
specYaml =
      StackSpecYaml
        { ssyDescription :: Maybe StackDescription
ssyDescription = Maybe StackDescription
gDescription
        , ssyTemplate :: FilePath
ssyTemplate = FilePath
templatePath
        , ssyDepends :: Maybe [StackName]
ssyDepends = Maybe [StackName]
gDepends
        , ssyActions :: Maybe [Action]
ssyActions = Maybe [Action]
gActions
        , ssyParameters :: Maybe ParametersYaml
ssyParameters = [ParameterYaml] -> ParametersYaml
parametersYaml forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Parameter -> Maybe ParameterYaml
parameterYaml forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe [Parameter]
gParameters
        , ssyCapabilities :: Maybe [Capability]
ssyCapabilities = Maybe [Capability]
gCapabilities
        , ssyTags :: Maybe TagsYaml
ssyTags = [TagYaml] -> TagsYaml
tagsYaml forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Tag -> TagYaml
TagYaml forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe [Tag]
gTags
        }

  FilePath
dir <- forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view forall a b. (a -> b) -> a -> b
$ forall env. HasDirectoryOption env => Lens' env DirectoryOption
directoryOptionL forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s a. (s -> a) -> SimpleGetter s a
to DirectoryOption -> FilePath
unDirectoryOption
  StackSpecPath
specPath <- forall env (m :: * -> *).
(MonadReader env m, HasAwsScope env) =>
StackName -> FilePath -> m StackSpecPath
buildSpecPath StackName
stackName FilePath
stackPath
  StackSpec
stackSpec <- forall env (m :: * -> *).
(MonadReader env m, HasConfig env) =>
FilePath -> StackSpecPath -> StackSpecYaml -> m StackSpec
buildStackSpec FilePath
dir StackSpecPath
specPath StackSpecYaml
specYaml

  forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
[Pair] -> m a -> m a
withThreadContext [Key
"stackName" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= StackSpec -> StackName
stackSpecStackName StackSpec
stackSpec] forall a b. (a -> b) -> a -> b
$ do
    forall (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
Bool -> StackSpec -> Maybe TemplateBody -> m ()
writeStackSpec Bool
gOverwrite StackSpec
stackSpec Maybe TemplateBody
mTemplateBody
    forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ StackSpecPath -> FilePath
stackSpecPathFilePath StackSpecPath
specPath