module Data.Apart.Usage.Blockchain
        (Transaction (..), Block, Blockchain, genesis, block, mainchain, verify) where

import "free" Control.Comonad.Cofree (Cofree (..))
import "base" Data.Bitraversable (Bitraversable (..))
import "base" Data.Functor.Compose (Compose (..))

import Data.Apart (Apart (..))
import Data.Apart.Shape (Shape (..))
import Data.Apart.Transformations (Scattered (..))
import Data.Apart.Structures.Stack (Stack)

type Account = Int
type Tokens = Int
data Connection
data Table

-- | Simplified transaction type, no certificates/keys.
data Transaction = Transaction
        { from :: Account, amount :: Tokens, to :: Account }

-- | Block is just a bunch of transactions, no pointers/keys here.
type Block = Stack Transaction

-- | Our blockchain type is distributed - we really don't want to keep all chain in memory,
-- instead, we'll store balance table of all accounts in some database.
type Blockchain = Scattered Stack Block (Connection, Table)

-- | Let's suppose that genesis is a regular transaction,
-- but proceeded from magic account 0 to real accounts.
--
-- @'Transaction' 0 1000 1 ':<' 'Nothing'@
genesis :: Block
genesis = Transaction 0 1000 1 :< Nothing

-- | First real block, 1 sends 50 tokens to 4.
--
-- @'Transaction' 1 50 4 ':<' 'Nothing'@
block :: Block
block = Transaction 1 50 4 :< Nothing

-- | We're facing with increasing chain in memory, so, let's calculate UTXO
-- for each account and put this information somewhere else.
--
-- @'Apart' $ 'block' ':<' 'Converted' ...@
mainchain :: Blockchain
mainchain = Apart $ block :< Converted (undefined, undefined)

-- | Verifying block is just a calculation UTXO for each account that trying to send money,
-- so, we already have balance table and current block, the task becomes simple -
-- just match transactions and balances.
verify :: Stack Transaction -> Blockchain -> Compose IO Maybe Block
verify txs chain = undefined