-- SPDX-FileCopyrightText: 2026 Alexandra de Wit
--
-- SPDX-License-Identifier: MIT

{- | The @amazonka@ environment every AWS adapter builds: credentials discovered the
standard AWS way, an optional region, and an optional endpoint override.

'AwsEndpoint' is the one endpoint-override record in the tree. The SQS backend, the S3
advisory client, and the CodeArtifact mint all reach @amazonka@ through 'newAwsEnv', so an
emulator or a VPC endpoint is configured the same way on every path.
-}
module Ecluse.Runtime.Aws.Env (
    AwsEndpoint (..),
    newAwsEnv,
) where

import Amazonka qualified as AWS

{- | Where an AWS-compatible endpoint lives, for pointing an adapter at a non-default host:
a local emulator (@ministack@) in tests, or a VPC endpoint.
-}
data AwsEndpoint = AwsEndpoint
    { AwsEndpoint -> Bool
endpointSecure :: Bool
    -- ^ Whether to connect over HTTPS (an emulator is usually plain HTTP).
    , AwsEndpoint -> Text
endpointHost :: Text
    -- ^ The host to connect to (e.g. @"localhost"@).
    , AwsEndpoint -> Int
endpointPort :: Int
    -- ^ The port to connect to (e.g. @4566@ for ministack).
    }
    deriving stock (AwsEndpoint -> AwsEndpoint -> Bool
(AwsEndpoint -> AwsEndpoint -> Bool)
-> (AwsEndpoint -> AwsEndpoint -> Bool) -> Eq AwsEndpoint
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: AwsEndpoint -> AwsEndpoint -> Bool
== :: AwsEndpoint -> AwsEndpoint -> Bool
$c/= :: AwsEndpoint -> AwsEndpoint -> Bool
/= :: AwsEndpoint -> AwsEndpoint -> Bool
Eq, Int -> AwsEndpoint -> ShowS
[AwsEndpoint] -> ShowS
AwsEndpoint -> String
(Int -> AwsEndpoint -> ShowS)
-> (AwsEndpoint -> String)
-> ([AwsEndpoint] -> ShowS)
-> Show AwsEndpoint
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AwsEndpoint -> ShowS
showsPrec :: Int -> AwsEndpoint -> ShowS
$cshow :: AwsEndpoint -> String
show :: AwsEndpoint -> String
$cshowList :: [AwsEndpoint] -> ShowS
showList :: [AwsEndpoint] -> ShowS
Show)

{- | Build an env for @service@. A region scopes it only when given, and an override
reconfigures @service@ only when given, so an absent value keeps @amazonka@'s own resolution.
-}
newAwsEnv :: Maybe Text -> Maybe AwsEndpoint -> AWS.Service -> IO AWS.Env
newAwsEnv :: Maybe Text -> Maybe AwsEndpoint -> Service -> IO Env
newAwsEnv Maybe Text
mRegion Maybe AwsEndpoint
mEndpoint Service
service = do
    base <- (EnvNoAuth -> IO Env) -> IO Env
forall (m :: * -> *). MonadIO m => (EnvNoAuth -> m Env) -> m Env
AWS.newEnv EnvNoAuth -> IO Env
forall (m :: * -> *) (withAuth :: * -> *).
(MonadCatch m, MonadIO m, Foldable withAuth) =>
Env' withAuth -> m Env
AWS.discover
    pure (overridden (scoped base))
  where
    scoped :: Env' withAuth -> Env' withAuth
scoped Env' withAuth
env = Env' withAuth
-> (Text -> Env' withAuth) -> Maybe Text -> Env' withAuth
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Env' withAuth
env (\Text
region -> Env' withAuth
env{AWS.region = AWS.Region' region}) Maybe Text
mRegion

    overridden :: Env' withAuth -> Env' withAuth
overridden Env' withAuth
env = Env' withAuth
-> (AwsEndpoint -> Env' withAuth)
-> Maybe AwsEndpoint
-> Env' withAuth
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Env' withAuth
env (\AwsEndpoint
endpoint -> Service -> Env' withAuth -> Env' withAuth
forall (withAuth :: * -> *).
Service -> Env' withAuth -> Env' withAuth
AWS.configureService (AwsEndpoint -> Service
pointedAt AwsEndpoint
endpoint) Env' withAuth
env) Maybe AwsEndpoint
mEndpoint

    pointedAt :: AwsEndpoint -> Service
pointedAt AwsEndpoint
endpoint =
        Bool -> ByteString -> Int -> Service -> Service
AWS.setEndpoint
            (AwsEndpoint -> Bool
endpointSecure AwsEndpoint
endpoint)
            (Text -> ByteString
forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 (AwsEndpoint -> Text
endpointHost AwsEndpoint
endpoint))
            (AwsEndpoint -> Int
endpointPort AwsEndpoint
endpoint)
            Service
service