{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module      : Language.Github.Actions.Job.Environment
-- Description : Deployment environment configuration for GitHub Actions jobs
-- Copyright   : (c) 2025 Bellroy Pty Ltd
-- License     : BSD-3-Clause
-- Maintainer  : Bellroy Tech Team <haskell@bellroy.com>
--
-- This module provides the 'JobEnvironment' type for configuring deployment
-- environments that jobs target in GitHub Actions workflows.
--
-- Job environments allow you to:
-- * Target specific deployment environments (production, staging, etc.)
-- * Control access through environment protection rules
-- * Use environment-specific secrets and variables
-- * Track deployment history and status
--
-- For more information about GitHub Actions environments, see:
-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idenvironment>
module Language.Github.Actions.Job.Environment
  ( JobEnvironment (..),
    gen,
  )
where

import Data.Aeson (FromJSON, ToJSON)
import qualified Data.Aeson as Aeson
import Data.Map (Map)
import Data.Text (Text)
import GHC.Generics (Generic)
import Hedgehog (MonadGen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

-- | Environment configuration for deployment jobs.
--
-- Job environments can be specified in two ways:
--
-- * 'NamedJobEnvironment' - Reference a named environment by string
-- * 'CustomJobEnvironment' - Configure environment with custom URL and other properties
--
-- Named environments are the most common and reference environments configured
-- in your repository settings. Custom environments allow inline configuration
-- for specific deployment scenarios.
--
-- Example usage:
--
-- @
-- import Language.Github.Actions.Job.Environment
-- import qualified Data.Map as Map
--
-- -- Reference a named environment
-- prodEnvironment :: JobEnvironment
-- prodEnvironment = NamedJobEnvironment "production"
--
-- stagingEnvironment :: JobEnvironment
-- stagingEnvironment = NamedJobEnvironment "staging"
--
-- -- Custom environment with URL
-- customEnvironment :: JobEnvironment
-- customEnvironment = CustomJobEnvironment $ Map.fromList
--  [ ("name", "review-pr-123")
--  , ("url", "https:\/\/pr-123.preview.example.com")
--  ]
-- @
--
-- For more details, see: <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idenvironment>
data JobEnvironment
  = -- | Reference a named environment
    NamedJobEnvironment Text
  | -- | Custom environment configuration
    CustomJobEnvironment (Map Text Text)
  deriving stock (JobEnvironment -> JobEnvironment -> Bool
(JobEnvironment -> JobEnvironment -> Bool)
-> (JobEnvironment -> JobEnvironment -> Bool) -> Eq JobEnvironment
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: JobEnvironment -> JobEnvironment -> Bool
== :: JobEnvironment -> JobEnvironment -> Bool
$c/= :: JobEnvironment -> JobEnvironment -> Bool
/= :: JobEnvironment -> JobEnvironment -> Bool
Eq, (forall x. JobEnvironment -> Rep JobEnvironment x)
-> (forall x. Rep JobEnvironment x -> JobEnvironment)
-> Generic JobEnvironment
forall x. Rep JobEnvironment x -> JobEnvironment
forall x. JobEnvironment -> Rep JobEnvironment x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. JobEnvironment -> Rep JobEnvironment x
from :: forall x. JobEnvironment -> Rep JobEnvironment x
$cto :: forall x. Rep JobEnvironment x -> JobEnvironment
to :: forall x. Rep JobEnvironment x -> JobEnvironment
Generic, Eq JobEnvironment
Eq JobEnvironment =>
(JobEnvironment -> JobEnvironment -> Ordering)
-> (JobEnvironment -> JobEnvironment -> Bool)
-> (JobEnvironment -> JobEnvironment -> Bool)
-> (JobEnvironment -> JobEnvironment -> Bool)
-> (JobEnvironment -> JobEnvironment -> Bool)
-> (JobEnvironment -> JobEnvironment -> JobEnvironment)
-> (JobEnvironment -> JobEnvironment -> JobEnvironment)
-> Ord JobEnvironment
JobEnvironment -> JobEnvironment -> Bool
JobEnvironment -> JobEnvironment -> Ordering
JobEnvironment -> JobEnvironment -> JobEnvironment
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: JobEnvironment -> JobEnvironment -> Ordering
compare :: JobEnvironment -> JobEnvironment -> Ordering
$c< :: JobEnvironment -> JobEnvironment -> Bool
< :: JobEnvironment -> JobEnvironment -> Bool
$c<= :: JobEnvironment -> JobEnvironment -> Bool
<= :: JobEnvironment -> JobEnvironment -> Bool
$c> :: JobEnvironment -> JobEnvironment -> Bool
> :: JobEnvironment -> JobEnvironment -> Bool
$c>= :: JobEnvironment -> JobEnvironment -> Bool
>= :: JobEnvironment -> JobEnvironment -> Bool
$cmax :: JobEnvironment -> JobEnvironment -> JobEnvironment
max :: JobEnvironment -> JobEnvironment -> JobEnvironment
$cmin :: JobEnvironment -> JobEnvironment -> JobEnvironment
min :: JobEnvironment -> JobEnvironment -> JobEnvironment
Ord, Int -> JobEnvironment -> ShowS
[JobEnvironment] -> ShowS
JobEnvironment -> String
(Int -> JobEnvironment -> ShowS)
-> (JobEnvironment -> String)
-> ([JobEnvironment] -> ShowS)
-> Show JobEnvironment
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> JobEnvironment -> ShowS
showsPrec :: Int -> JobEnvironment -> ShowS
$cshow :: JobEnvironment -> String
show :: JobEnvironment -> String
$cshowList :: [JobEnvironment] -> ShowS
showList :: [JobEnvironment] -> ShowS
Show)

instance FromJSON JobEnvironment where
  parseJSON :: Value -> Parser JobEnvironment
parseJSON Value
v = case Value
v of
    Aeson.String Text
s ->
      JobEnvironment -> Parser JobEnvironment
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (JobEnvironment -> Parser JobEnvironment)
-> JobEnvironment -> Parser JobEnvironment
forall a b. (a -> b) -> a -> b
$ Text -> JobEnvironment
NamedJobEnvironment Text
s
    Aeson.Object Object
o -> Map Text Text -> JobEnvironment
CustomJobEnvironment (Map Text Text -> JobEnvironment)
-> Parser (Map Text Text) -> Parser JobEnvironment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser (Map Text Text)
forall a. FromJSON a => Value -> Parser a
Aeson.parseJSON (Object -> Value
Aeson.Object Object
o)
    Value
_ -> String -> Parser JobEnvironment
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"JobEnvironment must be a string or an object"

instance ToJSON JobEnvironment where
  toJSON :: JobEnvironment -> Value
toJSON (NamedJobEnvironment Text
s) = Text -> Value
Aeson.String Text
s
  toJSON (CustomJobEnvironment Map Text Text
m) = Map Text Text -> Value
forall a. ToJSON a => a -> Value
Aeson.toJSON Map Text Text
m

gen :: (MonadGen m) => m JobEnvironment
gen :: forall (m :: * -> *). MonadGen m => m JobEnvironment
gen =
  [m JobEnvironment] -> m JobEnvironment
forall (m :: * -> *) a. MonadGen m => [m a] -> m a
Gen.choice
    [ Text -> JobEnvironment
NamedJobEnvironment (Text -> JobEnvironment) -> m Text -> m JobEnvironment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m Text
genText,
      Map Text Text -> JobEnvironment
CustomJobEnvironment (Map Text Text -> JobEnvironment)
-> m (Map Text Text) -> m JobEnvironment
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m (Map Text Text)
genTextMap
    ]
  where
    genText :: m Text
genText = Range Int -> m Char -> m Text
forall (m :: * -> *). MonadGen m => Range Int -> m Char -> m Text
Gen.text (Int -> Int -> Range Int
forall a. Integral a => a -> a -> Range a
Range.linear Int
1 Int
5) m Char
forall (m :: * -> *). MonadGen m => m Char
Gen.alphaNum
    genTextMap :: m (Map Text Text)
genTextMap = Range Int -> m (Text, Text) -> m (Map Text Text)
forall (m :: * -> *) k v.
(MonadGen m, Ord k) =>
Range Int -> m (k, v) -> m (Map k v)
Gen.map (Int -> Int -> Range Int
forall a. Integral a => a -> a -> Range a
Range.linear Int
1 Int
5) (m (Text, Text) -> m (Map Text Text))
-> m (Text, Text) -> m (Map Text Text)
forall a b. (a -> b) -> a -> b
$ (Text -> Text -> (Text, Text))
-> m Text -> m Text -> m (Text, Text)
forall a b c. (a -> b -> c) -> m a -> m b -> m c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,) m Text
genText m Text
genText