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

{- | The observation evidence and the page projections behind
"Ecluse.Runtime.Maintenance.CodeArtifact.Read", which documents the read-only half and
re-exports the curated surface. Importing this module opts out of that stability promise, the
convention @text@ and @bytestring@ use, so production code imports the public one.
-}
module Ecluse.Runtime.Maintenance.CodeArtifact.Read.Internal (
    -- * The calls that only observe
    ReadPlane (..),

    -- * Where an observation was made
    RepositoryIdentity (..),
    identityOfStore,

    -- * What one observation preserves
    VersionObservation (..),
    VersionOrigin (..),

    -- * One listing page
    observationsOfPage,
    versionsOfPage,
    storedOfObservation,
) where

import Amazonka.CodeArtifact qualified as CA
import Amazonka.CodeArtifact.Lens qualified as CAL
import Lens.Micro ((^.))

import Ecluse.Core.Ecosystem (Ecosystem)
import Ecluse.Core.Package (PackageName)
import Ecluse.Core.Registry.Maintenance (
    StoreFault,
    StoredVersion (..),
    VersionPresence,
 )
import Ecluse.Core.Registry.Maintenance.Upstream (UpstreamSafety)
import Ecluse.Core.Text (nonBlank)
import Ecluse.Core.Version (Version, mkVersion)
import Ecluse.Runtime.Maintenance.CodeArtifact.Decide (
    CodeArtifactStore (..),
    formatEcosystem,
    presenceOf,
 )

-- | The CodeArtifact calls that observe an inventory, held apart from every call that changes it.
data ReadPlane = ReadPlane
    { ReadPlane
-> ListPackages -> IO (Either StoreFault ListPackagesResponse)
rpListPackages :: CA.ListPackages -> IO (Either StoreFault CA.ListPackagesResponse)
    , ReadPlane
-> ListPackageVersions
-> IO (Either StoreFault ListPackageVersionsResponse)
rpListVersions :: CA.ListPackageVersions -> IO (Either StoreFault CA.ListPackageVersionsResponse)
    , ReadPlane
-> DescribeRepository
-> IO (Either StoreFault DescribeRepositoryResponse)
rpDescribeRepository :: CA.DescribeRepository -> IO (Either StoreFault CA.DescribeRepositoryResponse)
    , ReadPlane
-> DescribeRepository
-> IO (Either UpstreamSafety DescribeRepositoryResponse)
rpDescribeUpstream :: CA.DescribeRepository -> IO (Either UpstreamSafety CA.DescribeRepositoryResponse)
    {- ^ The same call as 'rpDescribeRepository', under the probe's own reading of a refusal:
    an identity that may not ask answers unsafe rather than faulting.
    -}
    , ReadPlane
-> ListTagsForResource
-> IO (Either StoreFault ListTagsForResourceResponse)
rpListTags :: CA.ListTagsForResource -> IO (Either StoreFault CA.ListTagsForResourceResponse)
    }

{- | The exact repository an observation was made in. The region is the store's own, which is
also the one the leaf binds its @amazonka@ environment to.
-}
data RepositoryIdentity = RepositoryIdentity
    { RepositoryIdentity -> Text
ridDomainOwner :: Text
    -- ^ The 12-digit account number that owns the domain.
    , RepositoryIdentity -> Text
ridRegion :: Text
    , RepositoryIdentity -> Text
ridDomain :: Text
    , RepositoryIdentity -> Text
ridRepository :: Text
    , RepositoryIdentity -> Ecosystem
ridEcosystem :: Ecosystem
    }
    deriving stock (RepositoryIdentity -> RepositoryIdentity -> Bool
(RepositoryIdentity -> RepositoryIdentity -> Bool)
-> (RepositoryIdentity -> RepositoryIdentity -> Bool)
-> Eq RepositoryIdentity
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RepositoryIdentity -> RepositoryIdentity -> Bool
== :: RepositoryIdentity -> RepositoryIdentity -> Bool
$c/= :: RepositoryIdentity -> RepositoryIdentity -> Bool
/= :: RepositoryIdentity -> RepositoryIdentity -> Bool
Eq, Int -> RepositoryIdentity -> ShowS
[RepositoryIdentity] -> ShowS
RepositoryIdentity -> String
(Int -> RepositoryIdentity -> ShowS)
-> (RepositoryIdentity -> String)
-> ([RepositoryIdentity] -> ShowS)
-> Show RepositoryIdentity
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RepositoryIdentity -> ShowS
showsPrec :: Int -> RepositoryIdentity -> ShowS
$cshow :: RepositoryIdentity -> String
show :: RepositoryIdentity -> String
$cshowList :: [RepositoryIdentity] -> ShowS
showList :: [RepositoryIdentity] -> ShowS
Show)

-- | The repository a store's coordinates name.
identityOfStore :: CodeArtifactStore -> RepositoryIdentity
identityOfStore :: CodeArtifactStore -> RepositoryIdentity
identityOfStore CodeArtifactStore
store =
    RepositoryIdentity
        { ridDomainOwner :: Text
ridDomainOwner = CodeArtifactStore -> Text
casDomainOwner CodeArtifactStore
store
        , ridRegion :: Text
ridRegion = CodeArtifactStore -> Text
casRegion CodeArtifactStore
store
        , ridDomain :: Text
ridDomain = CodeArtifactStore -> Text
casDomain CodeArtifactStore
store
        , ridRepository :: Text
ridRepository = CodeArtifactStore -> Text
casRepository CodeArtifactStore
store
        , ridEcosystem :: Ecosystem
ridEcosystem = CodeArtifactFormat -> Ecosystem
formatEcosystem (CodeArtifactStore -> CodeArtifactFormat
casFormat CodeArtifactStore
store)
        }

{- | How a version entered the domain, as the store reported it. Each field stays optional,
because one the store omitted is not one it denied.
-}
data VersionOrigin = VersionOrigin
    { VersionOrigin -> Maybe PackageVersionOriginType
vorType :: Maybe CA.PackageVersionOriginType
    -- ^ CodeArtifact's own token, verbatim, so a token this build does not know survives.
    , VersionOrigin -> Maybe Text
vorEntryRepository :: Maybe Text
    -- ^ The repository the version was first published to, where the store named one.
    , VersionOrigin -> Maybe Text
vorEntryConnection :: Maybe Text
    -- ^ The external connection the version was ingested through, where the store named one.
    }
    deriving stock (VersionOrigin -> VersionOrigin -> Bool
(VersionOrigin -> VersionOrigin -> Bool)
-> (VersionOrigin -> VersionOrigin -> Bool) -> Eq VersionOrigin
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: VersionOrigin -> VersionOrigin -> Bool
== :: VersionOrigin -> VersionOrigin -> Bool
$c/= :: VersionOrigin -> VersionOrigin -> Bool
/= :: VersionOrigin -> VersionOrigin -> Bool
Eq, Int -> VersionOrigin -> ShowS
[VersionOrigin] -> ShowS
VersionOrigin -> String
(Int -> VersionOrigin -> ShowS)
-> (VersionOrigin -> String)
-> ([VersionOrigin] -> ShowS)
-> Show VersionOrigin
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> VersionOrigin -> ShowS
showsPrec :: Int -> VersionOrigin -> ShowS
$cshow :: VersionOrigin -> String
show :: VersionOrigin -> String
$cshowList :: [VersionOrigin] -> ShowS
showList :: [VersionOrigin] -> ShowS
Show)

{- | One version the store reported holding, with the evidence a later decision reads. Two
observations of the same version in different repositories stay two observations.
-}
data VersionObservation = VersionObservation
    { VersionObservation -> RepositoryIdentity
obsIdentity :: RepositoryIdentity
    , VersionObservation -> PackageName
obsPackage :: PackageName
    , VersionObservation -> Version
obsVersion :: Version
    , VersionObservation -> PackageVersionStatus
obsStatus :: CA.PackageVersionStatus
    -- ^ CodeArtifact's own status, kept beside the projection so an unexpected one survives.
    , VersionObservation -> VersionPresence
obsPresence :: VersionPresence
    , VersionObservation -> Maybe Text
obsRevision :: Maybe Text
    -- ^ The store's revision of this version, 'Nothing' where it reported none.
    , VersionObservation -> Maybe VersionOrigin
obsOrigin :: Maybe VersionOrigin
    -- ^ 'Nothing' where the store reported no origin at all.
    }
    deriving stock (VersionObservation -> VersionObservation -> Bool
(VersionObservation -> VersionObservation -> Bool)
-> (VersionObservation -> VersionObservation -> Bool)
-> Eq VersionObservation
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: VersionObservation -> VersionObservation -> Bool
== :: VersionObservation -> VersionObservation -> Bool
$c/= :: VersionObservation -> VersionObservation -> Bool
/= :: VersionObservation -> VersionObservation -> Bool
Eq, Int -> VersionObservation -> ShowS
[VersionObservation] -> ShowS
VersionObservation -> String
(Int -> VersionObservation -> ShowS)
-> (VersionObservation -> String)
-> ([VersionObservation] -> ShowS)
-> Show VersionObservation
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> VersionObservation -> ShowS
showsPrec :: Int -> VersionObservation -> ShowS
$cshow :: VersionObservation -> String
show :: VersionObservation -> String
$cshowList :: [VersionObservation] -> ShowS
showList :: [VersionObservation] -> ShowS
Show)

-- | The observations in one listing page, one per entry and never merged by version.
observationsOfPage :: RepositoryIdentity -> PackageName -> [CA.PackageVersionSummary] -> [VersionObservation]
observationsOfPage :: RepositoryIdentity
-> PackageName -> [PackageVersionSummary] -> [VersionObservation]
observationsOfPage RepositoryIdentity
repository PackageName
name = (PackageVersionSummary -> VersionObservation)
-> [PackageVersionSummary] -> [VersionObservation]
forall a b. (a -> b) -> [a] -> [b]
map PackageVersionSummary -> VersionObservation
observed
  where
    observed :: PackageVersionSummary -> VersionObservation
observed PackageVersionSummary
summary =
        VersionObservation
            { obsIdentity :: RepositoryIdentity
obsIdentity = RepositoryIdentity
repository
            , obsPackage :: PackageName
obsPackage = PackageName
name
            , obsVersion :: Version
obsVersion = Ecosystem -> Text -> Version
mkVersion (RepositoryIdentity -> Ecosystem
ridEcosystem RepositoryIdentity
repository) (PackageVersionSummary
summary PackageVersionSummary
-> Getting Text PackageVersionSummary Text -> Text
forall s a. s -> Getting a s a -> a
^. Getting Text PackageVersionSummary Text
Lens' PackageVersionSummary Text
CAL.packageVersionSummary_version)
            , obsStatus :: PackageVersionStatus
obsStatus = PackageVersionSummary
summary PackageVersionSummary
-> Getting
     PackageVersionStatus PackageVersionSummary PackageVersionStatus
-> PackageVersionStatus
forall s a. s -> Getting a s a -> a
^. Getting
  PackageVersionStatus PackageVersionSummary PackageVersionStatus
Lens' PackageVersionSummary PackageVersionStatus
CAL.packageVersionSummary_status
            , obsPresence :: VersionPresence
obsPresence = PackageVersionStatus -> VersionPresence
presenceOf (PackageVersionSummary
summary PackageVersionSummary
-> Getting
     PackageVersionStatus PackageVersionSummary PackageVersionStatus
-> PackageVersionStatus
forall s a. s -> Getting a s a -> a
^. Getting
  PackageVersionStatus PackageVersionSummary PackageVersionStatus
Lens' PackageVersionSummary PackageVersionStatus
CAL.packageVersionSummary_status)
            , obsRevision :: Maybe Text
obsRevision = Text -> Maybe Text
nonBlank (Text -> Maybe Text) -> Maybe Text -> Maybe Text
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< PackageVersionSummary
summary PackageVersionSummary
-> Getting (Maybe Text) PackageVersionSummary (Maybe Text)
-> Maybe Text
forall s a. s -> Getting a s a -> a
^. Getting (Maybe Text) PackageVersionSummary (Maybe Text)
Lens' PackageVersionSummary (Maybe Text)
CAL.packageVersionSummary_revision
            , obsOrigin :: Maybe VersionOrigin
obsOrigin = PackageVersionOrigin -> VersionOrigin
originOf (PackageVersionOrigin -> VersionOrigin)
-> Maybe PackageVersionOrigin -> Maybe VersionOrigin
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> PackageVersionSummary
summary PackageVersionSummary
-> Getting
     (Maybe PackageVersionOrigin)
     PackageVersionSummary
     (Maybe PackageVersionOrigin)
-> Maybe PackageVersionOrigin
forall s a. s -> Getting a s a -> a
^. Getting
  (Maybe PackageVersionOrigin)
  PackageVersionSummary
  (Maybe PackageVersionOrigin)
Lens' PackageVersionSummary (Maybe PackageVersionOrigin)
CAL.packageVersionSummary_origin
            }

-- | The versions in one listing page, projected to what the sweep reads.
versionsOfPage :: RepositoryIdentity -> PackageName -> [CA.PackageVersionSummary] -> [StoredVersion]
versionsOfPage :: RepositoryIdentity
-> PackageName -> [PackageVersionSummary] -> [StoredVersion]
versionsOfPage RepositoryIdentity
repository PackageName
name = (VersionObservation -> StoredVersion)
-> [VersionObservation] -> [StoredVersion]
forall a b. (a -> b) -> [a] -> [b]
map VersionObservation -> StoredVersion
storedOfObservation ([VersionObservation] -> [StoredVersion])
-> ([PackageVersionSummary] -> [VersionObservation])
-> [PackageVersionSummary]
-> [StoredVersion]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RepositoryIdentity
-> PackageName -> [PackageVersionSummary] -> [VersionObservation]
observationsOfPage RepositoryIdentity
repository PackageName
name

-- | The sweep's view of one observation: which version, and whether the store still serves it.
storedOfObservation :: VersionObservation -> StoredVersion
storedOfObservation :: VersionObservation -> StoredVersion
storedOfObservation VersionObservation
observation =
    StoredVersion
        { storedVersion :: Version
storedVersion = VersionObservation -> Version
obsVersion VersionObservation
observation
        , storedPresence :: VersionPresence
storedPresence = VersionObservation -> VersionPresence
obsPresence VersionObservation
observation
        , storedRevision :: Maybe Text
storedRevision = VersionObservation -> Maybe Text
obsRevision VersionObservation
observation
        }

-- A blank entry point names nothing, so it reads as absent rather than as an empty name.
originOf :: CA.PackageVersionOrigin -> VersionOrigin
originOf :: PackageVersionOrigin -> VersionOrigin
originOf PackageVersionOrigin
origin =
    VersionOrigin
        { vorType :: Maybe PackageVersionOriginType
vorType = PackageVersionOrigin
origin PackageVersionOrigin
-> Getting
     (Maybe PackageVersionOriginType)
     PackageVersionOrigin
     (Maybe PackageVersionOriginType)
-> Maybe PackageVersionOriginType
forall s a. s -> Getting a s a -> a
^. Getting
  (Maybe PackageVersionOriginType)
  PackageVersionOrigin
  (Maybe PackageVersionOriginType)
Lens' PackageVersionOrigin (Maybe PackageVersionOriginType)
CAL.packageVersionOrigin_originType
        , vorEntryRepository :: Maybe Text
vorEntryRepository = Text -> Maybe Text
nonBlank (Text -> Maybe Text) -> Maybe Text -> Maybe Text
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< (Maybe DomainEntryPoint
entry Maybe DomainEntryPoint
-> (DomainEntryPoint -> Maybe Text) -> Maybe Text
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (DomainEntryPoint
-> Getting (Maybe Text) DomainEntryPoint (Maybe Text) -> Maybe Text
forall s a. s -> Getting a s a -> a
^. Getting (Maybe Text) DomainEntryPoint (Maybe Text)
Lens' DomainEntryPoint (Maybe Text)
CAL.domainEntryPoint_repositoryName))
        , vorEntryConnection :: Maybe Text
vorEntryConnection = Text -> Maybe Text
nonBlank (Text -> Maybe Text) -> Maybe Text -> Maybe Text
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< (Maybe DomainEntryPoint
entry Maybe DomainEntryPoint
-> (DomainEntryPoint -> Maybe Text) -> Maybe Text
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (DomainEntryPoint
-> Getting (Maybe Text) DomainEntryPoint (Maybe Text) -> Maybe Text
forall s a. s -> Getting a s a -> a
^. Getting (Maybe Text) DomainEntryPoint (Maybe Text)
Lens' DomainEntryPoint (Maybe Text)
CAL.domainEntryPoint_externalConnectionName))
        }
  where
    entry :: Maybe DomainEntryPoint
entry = PackageVersionOrigin
origin PackageVersionOrigin
-> Getting
     (Maybe DomainEntryPoint)
     PackageVersionOrigin
     (Maybe DomainEntryPoint)
-> Maybe DomainEntryPoint
forall s a. s -> Getting a s a -> a
^. Getting
  (Maybe DomainEntryPoint)
  PackageVersionOrigin
  (Maybe DomainEntryPoint)
Lens' PackageVersionOrigin (Maybe DomainEntryPoint)
CAL.packageVersionOrigin_domainEntryPoint