module U.Codebase.Sqlite.Branch.Format
  ( BranchFormat' (..),
    BranchFormat,
    HashBranchFormat,
    BranchLocalIds,
    BranchLocalIds' (..),
    HashBranchLocalIds,
    SyncBranchFormat,
    SyncBranchFormat' (..),
    LocalBranchBytes (..),
    localToDbBranch,
    localToDbDiff,
    localToHashBranch,
    localToBranch,
    -- dbToLocalDiff,
  )
where

import Data.Vector (Vector)
import Data.Vector qualified as Vector
import U.Codebase.HashTags
import U.Codebase.Sqlite.Branch.Diff (Diff, LocalDiff)
import U.Codebase.Sqlite.Branch.Diff qualified as Branch.Diff
import U.Codebase.Sqlite.Branch.Full (DbBranch, HashBranch, LocalBranch)
import U.Codebase.Sqlite.Branch.Full qualified as Branch.Full
import U.Codebase.Sqlite.DbId (BranchObjectId, CausalHashId, ObjectId, PatchObjectId, TextId)
import U.Codebase.Sqlite.LocalIds
  ( LocalBranchChildId (..),
    LocalDefnId (..),
    LocalPatchObjectId (..),
    LocalTextId (..),
  )
import Unison.Prelude

-- | A 'BranchFormat' is a deserialized namespace object (@object.bytes@).
--
-- you can use the exact same `BranchLocalIds` when converting between `Full` and `Diff`
data
  BranchFormat'
    text
    defRef
    patchRef
    childRef
    branchRef
  = Full (BranchLocalIds' text defRef patchRef childRef) LocalBranch
  | Diff branchRef (BranchLocalIds' text defRef patchRef childRef) LocalDiff
  deriving (Int
-> BranchFormat' text defRef patchRef childRef branchRef -> ShowS
[BranchFormat' text defRef patchRef childRef branchRef] -> ShowS
BranchFormat' text defRef patchRef childRef branchRef -> String
(Int
 -> BranchFormat' text defRef patchRef childRef branchRef -> ShowS)
-> (BranchFormat' text defRef patchRef childRef branchRef
    -> String)
-> ([BranchFormat' text defRef patchRef childRef branchRef]
    -> ShowS)
-> Show (BranchFormat' text defRef patchRef childRef branchRef)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall text defRef patchRef childRef branchRef.
(Show text, Show defRef, Show patchRef, Show childRef,
 Show branchRef) =>
Int
-> BranchFormat' text defRef patchRef childRef branchRef -> ShowS
forall text defRef patchRef childRef branchRef.
(Show text, Show defRef, Show patchRef, Show childRef,
 Show branchRef) =>
[BranchFormat' text defRef patchRef childRef branchRef] -> ShowS
forall text defRef patchRef childRef branchRef.
(Show text, Show defRef, Show patchRef, Show childRef,
 Show branchRef) =>
BranchFormat' text defRef patchRef childRef branchRef -> String
$cshowsPrec :: forall text defRef patchRef childRef branchRef.
(Show text, Show defRef, Show patchRef, Show childRef,
 Show branchRef) =>
Int
-> BranchFormat' text defRef patchRef childRef branchRef -> ShowS
showsPrec :: Int
-> BranchFormat' text defRef patchRef childRef branchRef -> ShowS
$cshow :: forall text defRef patchRef childRef branchRef.
(Show text, Show defRef, Show patchRef, Show childRef,
 Show branchRef) =>
BranchFormat' text defRef patchRef childRef branchRef -> String
show :: BranchFormat' text defRef patchRef childRef branchRef -> String
$cshowList :: forall text defRef patchRef childRef branchRef.
(Show text, Show defRef, Show patchRef, Show childRef,
 Show branchRef) =>
[BranchFormat' text defRef patchRef childRef branchRef] -> ShowS
showList :: [BranchFormat' text defRef patchRef childRef branchRef] -> ShowS
Show)

-- | The 'BranchFormat'' used to store a branch in Sqlite
type BranchFormat = BranchFormat' TextId ObjectId PatchObjectId (BranchObjectId, CausalHashId) BranchObjectId

-- | A BranchFormat which uses Hashes and Text for all its references, no
-- Ids which are specific to a particular codebase.
type HashBranchFormat = BranchFormat' Text ComponentHash PatchHash (BranchHash, CausalHash)

-- = Full BranchLocalIds LocalBranch
-- \| Diff BranchObjectId BranchLocalIds LocalDiff

-- | A 'BranchLocalIds' is a mapping between local ids (local to this object) encoded as offsets, and actual database ids.
--
-- For example, a @branchTextLookup@ vector of @[50, 74]@ means "local id 0 corresponds to database text id 50, and
-- local id 1 corresponds to database text id 74".
type BranchLocalIds = BranchLocalIds' TextId ObjectId PatchObjectId (BranchObjectId, CausalHashId)

type HashBranchLocalIds = BranchLocalIds' Text ComponentHash PatchHash (BranchHash, CausalHash)

-- temp_entity
--  branch #foo
--
-- temp_entity_missing_dependency
--   #foo depends on causal #bar
--   #foo depends on namespace #baz
--
-- 1. store causal #bar, go to flush dependencies like normal
-- 2. ... oh this case is different than the others - we don't want to delete that row

----
-- can't simply treat causal's value hash as a mandatory dependency because we can't be sure
-- that the causal doesn't already exist in the target codebase without the value.
-- it probably does exist together with the value (though we found cases in the past where it didn't
-- due to race conditions, but we fixed that and added transactions and it shouldn't happen again?),
-- but it's not enforced at the schema level.
-- to enforce it at the schema level, we'd have to do something like store namespace_object_id instead
-- of value_hash in causal, which would require a db migration for a thing we don't necessarily even want
-- long term.
-- so, we can't simply "require" the value hash as a dependency of the causals and expect things to work smoothly
-- without relying on prayer.
--
-- temp_entity
--  branch #foo
--  causal #bar

-- temp_entity_missing_dependency
--   #foo depends on causal #bar
--   #bar depends on namespace #baz
--

data BranchLocalIds' t d p c = LocalIds
  { forall t d p c. BranchLocalIds' t d p c -> Vector t
branchTextLookup :: Vector t,
    forall t d p c. BranchLocalIds' t d p c -> Vector d
branchDefnLookup :: Vector d,
    forall t d p c. BranchLocalIds' t d p c -> Vector p
branchPatchLookup :: Vector p,
    forall t d p c. BranchLocalIds' t d p c -> Vector c
branchChildLookup :: Vector c
  }
  deriving (Int -> BranchLocalIds' t d p c -> ShowS
[BranchLocalIds' t d p c] -> ShowS
BranchLocalIds' t d p c -> String
(Int -> BranchLocalIds' t d p c -> ShowS)
-> (BranchLocalIds' t d p c -> String)
-> ([BranchLocalIds' t d p c] -> ShowS)
-> Show (BranchLocalIds' t d p c)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall t d p c.
(Show t, Show d, Show p, Show c) =>
Int -> BranchLocalIds' t d p c -> ShowS
forall t d p c.
(Show t, Show d, Show p, Show c) =>
[BranchLocalIds' t d p c] -> ShowS
forall t d p c.
(Show t, Show d, Show p, Show c) =>
BranchLocalIds' t d p c -> String
$cshowsPrec :: forall t d p c.
(Show t, Show d, Show p, Show c) =>
Int -> BranchLocalIds' t d p c -> ShowS
showsPrec :: Int -> BranchLocalIds' t d p c -> ShowS
$cshow :: forall t d p c.
(Show t, Show d, Show p, Show c) =>
BranchLocalIds' t d p c -> String
show :: BranchLocalIds' t d p c -> String
$cshowList :: forall t d p c.
(Show t, Show d, Show p, Show c) =>
[BranchLocalIds' t d p c] -> ShowS
showList :: [BranchLocalIds' t d p c] -> ShowS
Show)

-- | Bytes encoding a LocalBranch
newtype LocalBranchBytes = LocalBranchBytes ByteString
  deriving (Int -> LocalBranchBytes -> ShowS
[LocalBranchBytes] -> ShowS
LocalBranchBytes -> String
(Int -> LocalBranchBytes -> ShowS)
-> (LocalBranchBytes -> String)
-> ([LocalBranchBytes] -> ShowS)
-> Show LocalBranchBytes
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> LocalBranchBytes -> ShowS
showsPrec :: Int -> LocalBranchBytes -> ShowS
$cshow :: LocalBranchBytes -> String
show :: LocalBranchBytes -> String
$cshowList :: [LocalBranchBytes] -> ShowS
showList :: [LocalBranchBytes] -> ShowS
Show, LocalBranchBytes -> LocalBranchBytes -> Bool
(LocalBranchBytes -> LocalBranchBytes -> Bool)
-> (LocalBranchBytes -> LocalBranchBytes -> Bool)
-> Eq LocalBranchBytes
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: LocalBranchBytes -> LocalBranchBytes -> Bool
== :: LocalBranchBytes -> LocalBranchBytes -> Bool
$c/= :: LocalBranchBytes -> LocalBranchBytes -> Bool
/= :: LocalBranchBytes -> LocalBranchBytes -> Bool
Eq, Eq LocalBranchBytes
Eq LocalBranchBytes =>
(LocalBranchBytes -> LocalBranchBytes -> Ordering)
-> (LocalBranchBytes -> LocalBranchBytes -> Bool)
-> (LocalBranchBytes -> LocalBranchBytes -> Bool)
-> (LocalBranchBytes -> LocalBranchBytes -> Bool)
-> (LocalBranchBytes -> LocalBranchBytes -> Bool)
-> (LocalBranchBytes -> LocalBranchBytes -> LocalBranchBytes)
-> (LocalBranchBytes -> LocalBranchBytes -> LocalBranchBytes)
-> Ord LocalBranchBytes
LocalBranchBytes -> LocalBranchBytes -> Bool
LocalBranchBytes -> LocalBranchBytes -> Ordering
LocalBranchBytes -> LocalBranchBytes -> LocalBranchBytes
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 :: LocalBranchBytes -> LocalBranchBytes -> Ordering
compare :: LocalBranchBytes -> LocalBranchBytes -> Ordering
$c< :: LocalBranchBytes -> LocalBranchBytes -> Bool
< :: LocalBranchBytes -> LocalBranchBytes -> Bool
$c<= :: LocalBranchBytes -> LocalBranchBytes -> Bool
<= :: LocalBranchBytes -> LocalBranchBytes -> Bool
$c> :: LocalBranchBytes -> LocalBranchBytes -> Bool
> :: LocalBranchBytes -> LocalBranchBytes -> Bool
$c>= :: LocalBranchBytes -> LocalBranchBytes -> Bool
>= :: LocalBranchBytes -> LocalBranchBytes -> Bool
$cmax :: LocalBranchBytes -> LocalBranchBytes -> LocalBranchBytes
max :: LocalBranchBytes -> LocalBranchBytes -> LocalBranchBytes
$cmin :: LocalBranchBytes -> LocalBranchBytes -> LocalBranchBytes
min :: LocalBranchBytes -> LocalBranchBytes -> LocalBranchBytes
Ord)

data SyncBranchFormat' parent text defn patch child
  = SyncFull (BranchLocalIds' text defn patch child) LocalBranchBytes
  | SyncDiff parent (BranchLocalIds' text defn patch child) LocalBranchBytes

type SyncBranchFormat = SyncBranchFormat' BranchObjectId TextId ObjectId PatchObjectId (BranchObjectId, CausalHashId)

localToBranch :: (Ord t, Ord d) => BranchLocalIds' t d p c -> LocalBranch -> (Branch.Full.Branch' t d p c)
localToBranch :: forall t d p c.
(Ord t, Ord d) =>
BranchLocalIds' t d p c -> LocalBranch -> Branch' t d p c
localToBranch BranchLocalIds' t d p c
li =
  (LocalTextId -> t)
-> (LocalDefnId -> d)
-> (LocalPatchObjectId -> p)
-> (LocalBranchChildId -> c)
-> LocalBranch
-> Branch' t d p c
forall t h p c t' h' p' c'.
(Ord t', Ord h') =>
(t -> t')
-> (h -> h')
-> (p -> p')
-> (c -> c')
-> Branch' t h p c
-> Branch' t' h' p' c'
Branch.Full.quadmap (BranchLocalIds' t d p c -> LocalTextId -> t
forall t d p c. BranchLocalIds' t d p c -> LocalTextId -> t
lookupBranchLocalText BranchLocalIds' t d p c
li) (BranchLocalIds' t d p c -> LocalDefnId -> d
forall t d p c. BranchLocalIds' t d p c -> LocalDefnId -> d
lookupBranchLocalDefn BranchLocalIds' t d p c
li) (BranchLocalIds' t d p c -> LocalPatchObjectId -> p
forall t d p c. BranchLocalIds' t d p c -> LocalPatchObjectId -> p
lookupBranchLocalPatch BranchLocalIds' t d p c
li) (BranchLocalIds' t d p c -> LocalBranchChildId -> c
forall t d p c. BranchLocalIds' t d p c -> LocalBranchChildId -> c
lookupBranchLocalChild BranchLocalIds' t d p c
li)

localToDbBranch :: BranchLocalIds -> LocalBranch -> DbBranch
localToDbBranch :: BranchLocalIds -> LocalBranch -> DbBranch
localToDbBranch = BranchLocalIds -> LocalBranch -> DbBranch
forall t d p c.
(Ord t, Ord d) =>
BranchLocalIds' t d p c -> LocalBranch -> Branch' t d p c
localToBranch

localToHashBranch :: HashBranchLocalIds -> LocalBranch -> HashBranch
localToHashBranch :: HashBranchLocalIds -> LocalBranch -> HashBranch
localToHashBranch = HashBranchLocalIds -> LocalBranch -> HashBranch
forall t d p c.
(Ord t, Ord d) =>
BranchLocalIds' t d p c -> LocalBranch -> Branch' t d p c
localToBranch

localToDbDiff :: BranchLocalIds -> LocalDiff -> Diff
localToDbDiff :: BranchLocalIds -> LocalDiff -> Diff
localToDbDiff BranchLocalIds
li =
  (LocalTextId -> TextId)
-> (LocalDefnId -> ObjectId)
-> (LocalPatchObjectId -> PatchObjectId)
-> (LocalBranchChildId -> (BranchObjectId, CausalHashId))
-> LocalDiff
-> Diff
forall t' h' t h p p' c c'.
(Ord t', Ord h') =>
(t -> t')
-> (h -> h')
-> (p -> p')
-> (c -> c')
-> Diff' t h p c
-> Diff' t' h' p' c'
Branch.Diff.quadmap (BranchLocalIds -> LocalTextId -> TextId
forall t d p c. BranchLocalIds' t d p c -> LocalTextId -> t
lookupBranchLocalText BranchLocalIds
li) (BranchLocalIds -> LocalDefnId -> ObjectId
forall t d p c. BranchLocalIds' t d p c -> LocalDefnId -> d
lookupBranchLocalDefn BranchLocalIds
li) (BranchLocalIds -> LocalPatchObjectId -> PatchObjectId
forall t d p c. BranchLocalIds' t d p c -> LocalPatchObjectId -> p
lookupBranchLocalPatch BranchLocalIds
li) (BranchLocalIds
-> LocalBranchChildId -> (BranchObjectId, CausalHashId)
forall t d p c. BranchLocalIds' t d p c -> LocalBranchChildId -> c
lookupBranchLocalChild BranchLocalIds
li)

lookupBranchLocalText :: BranchLocalIds' t d p c -> LocalTextId -> t
lookupBranchLocalText :: forall t d p c. BranchLocalIds' t d p c -> LocalTextId -> t
lookupBranchLocalText BranchLocalIds' t d p c
li (LocalTextId Word64
w) = BranchLocalIds' t d p c -> Vector t
forall t d p c. BranchLocalIds' t d p c -> Vector t
branchTextLookup BranchLocalIds' t d p c
li Vector t -> Int -> t
forall a. Vector a -> Int -> a
Vector.! Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
w

lookupBranchLocalDefn :: BranchLocalIds' t d p c -> LocalDefnId -> d
lookupBranchLocalDefn :: forall t d p c. BranchLocalIds' t d p c -> LocalDefnId -> d
lookupBranchLocalDefn BranchLocalIds' t d p c
li (LocalDefnId Word64
w) = BranchLocalIds' t d p c -> Vector d
forall t d p c. BranchLocalIds' t d p c -> Vector d
branchDefnLookup BranchLocalIds' t d p c
li Vector d -> Int -> d
forall a. Vector a -> Int -> a
Vector.! Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
w

lookupBranchLocalPatch :: BranchLocalIds' t d p c -> LocalPatchObjectId -> p
lookupBranchLocalPatch :: forall t d p c. BranchLocalIds' t d p c -> LocalPatchObjectId -> p
lookupBranchLocalPatch BranchLocalIds' t d p c
li (LocalPatchObjectId Word64
w) = BranchLocalIds' t d p c -> Vector p
forall t d p c. BranchLocalIds' t d p c -> Vector p
branchPatchLookup BranchLocalIds' t d p c
li Vector p -> Int -> p
forall a. Vector a -> Int -> a
Vector.! Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
w

lookupBranchLocalChild :: BranchLocalIds' t d p c -> LocalBranchChildId -> c
lookupBranchLocalChild :: forall t d p c. BranchLocalIds' t d p c -> LocalBranchChildId -> c
lookupBranchLocalChild BranchLocalIds' t d p c
li (LocalBranchChildId Word64
w) = BranchLocalIds' t d p c -> Vector c
forall t d p c. BranchLocalIds' t d p c -> Vector c
branchChildLookup BranchLocalIds' t d p c
li Vector c -> Int -> c
forall a. Vector a -> Int -> a
Vector.! Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
w