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

{- | A full read's packed packument: its small top-level members as aeson's tree, and every retained
release packed against the document's table. Each release already holds the source author pointer.
-}
module Ecluse.Core.Registry.Npm.Document (
    PackedPackument (..),
    packumentValue,
    packumentResident,
    tarballHole,
    tarballUrl,
    withTarball,
) where

import Data.Aeson (Value (Object, String))
import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap (KeyMap)
import Data.Aeson.KeyMap qualified as KeyMap

import Ecluse.Core.Registry.Json.Packed (DocTable, Packed, packedResident, packedValue, tableResident)
import Ecluse.Core.Text (textStorageBytes)

-- | The top-level members other than @versions@, the table, and each release by version key.
data PackedPackument = PackedPackument
    { PackedPackument -> KeyMap Value
packumentTop :: KeyMap Value
    , PackedPackument -> DocTable
packumentTable :: DocTable
    , PackedPackument -> KeyMap Packed
packumentVersions :: KeyMap Packed
    }
    deriving stock (PackedPackument -> PackedPackument -> Bool
(PackedPackument -> PackedPackument -> Bool)
-> (PackedPackument -> PackedPackument -> Bool)
-> Eq PackedPackument
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PackedPackument -> PackedPackument -> Bool
== :: PackedPackument -> PackedPackument -> Bool
$c/= :: PackedPackument -> PackedPackument -> Bool
/= :: PackedPackument -> PackedPackument -> Bool
Eq, Int -> PackedPackument -> ShowS
[PackedPackument] -> ShowS
PackedPackument -> String
(Int -> PackedPackument -> ShowS)
-> (PackedPackument -> String)
-> ([PackedPackument] -> ShowS)
-> Show PackedPackument
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PackedPackument -> ShowS
showsPrec :: Int -> PackedPackument -> ShowS
$cshow :: PackedPackument -> String
show :: PackedPackument -> String
$cshowList :: [PackedPackument] -> ShowS
showList :: [PackedPackument] -> ShowS
Show)

{- | The packument as aeson's tree, as the read would have built it, or nothing when a release names a
string its table lacks.
-}
packumentValue :: PackedPackument -> Maybe Value
packumentValue :: PackedPackument -> Maybe Value
packumentValue PackedPackument
packument =
    (\KeyMap Value
versions -> KeyMap Value -> Value
Object (Key -> Value -> KeyMap Value -> KeyMap Value
forall v. Key -> v -> KeyMap v -> KeyMap v
KeyMap.insert Key
"versions" (KeyMap Value -> Value
Object KeyMap Value
versions) (PackedPackument -> KeyMap Value
packumentTop PackedPackument
packument)))
        (KeyMap Value -> Value) -> Maybe (KeyMap Value) -> Maybe Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Packed -> Maybe Value) -> KeyMap Packed -> Maybe (KeyMap Value)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> KeyMap a -> f (KeyMap b)
traverse (DocTable -> Packed -> Maybe Value
packedValue (PackedPackument -> DocTable
packumentTable PackedPackument
packument)) (PackedPackument -> KeyMap Packed
packumentVersions PackedPackument
packument)

{- | The heap bytes the table and the releases hold: each release with its map node, its version key's
text and array, and its packed value.
-}
packumentResident :: PackedPackument -> Int
packumentResident :: PackedPackument -> Int
packumentResident PackedPackument
packument =
    DocTable -> Int
tableResident (PackedPackument -> DocTable
packumentTable PackedPackument
packument)
        Int -> Int -> Int
forall a. Num a => a -> a -> a
+ [Int] -> Int
forall a (f :: * -> *). (Foldable f, Num a) => f a -> a
sum [Int
104 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Text -> Int
textStorageBytes (Key -> Text
Key.toText Key
version) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Packed -> Int
packedResident Packed
release | (Key
version, Packed
release) <- KeyMap Packed -> [(Key, Packed)]
forall v. KeyMap v -> [(Key, v)]
KeyMap.toList (PackedPackument -> KeyMap Packed
packumentVersions PackedPackument
packument)]

-- | The member path of the string a served release rebases.
tarballHole :: [Text]
tarballHole :: [Text]
tarballHole = [Text
"dist", Text
"tarball"]

-- | The string at 'tarballHole' in a release, when it holds one there.
tarballUrl :: Value -> Maybe Text
tarballUrl :: Value -> Maybe Text
tarballUrl = [Text] -> Value -> Maybe Text
go [Text]
tarballHole
  where
    go :: [Text] -> Value -> Maybe Text
go [Text]
path Value
value = case ([Text]
path, Value
value) of
        ([], String Text
url) -> Text -> Maybe Text
forall a. a -> Maybe a
Just Text
url
        (Text
name : [Text]
rest, Object KeyMap Value
fields) -> Key -> KeyMap Value -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup (Text -> Key
Key.fromText Text
name) KeyMap Value
fields Maybe Value -> (Value -> 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
>>= [Text] -> Value -> Maybe Text
go [Text]
rest
        ([Text], Value)
_ -> Maybe Text
forall a. Maybe a
Nothing

-- | The release with the string at 'tarballHole' replaced, when it holds one there.
withTarball :: Text -> Value -> Value
withTarball :: Text -> Value -> Value
withTarball Text
url = [Text] -> Value -> Value
go [Text]
tarballHole
  where
    go :: [Text] -> Value -> Value
go [Text]
path Value
value = case ([Text]
path, Value
value) of
        ([], String Text
_) -> Text -> Value
String Text
url
        (Text
name : [Text]
rest, Object KeyMap Value
fields)
            | Just Value
member <- Key -> KeyMap Value -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup (Text -> Key
Key.fromText Text
name) KeyMap Value
fields -> KeyMap Value -> Value
Object (Key -> Value -> KeyMap Value -> KeyMap Value
forall v. Key -> v -> KeyMap v -> KeyMap v
KeyMap.insert (Text -> Key
Key.fromText Text
name) ([Text] -> Value -> Value
go [Text]
rest Value
member) KeyMap Value
fields)
        ([Text], Value)
_ -> Value
value