module Network.Haskoin.Wallet.Block where

import           Control.Exception                  (throw)
import           Control.Monad.Catch                (MonadThrow, throwM)
import           Control.Monad.Trans                (MonadIO)
import           Data.Maybe                         (fromMaybe)
import           Database.Persist.Sql               (SqlPersistT)
import           Network.Haskoin.Block
import           Network.Haskoin.Node.HeaderTree
import           Network.Haskoin.Wallet.Model
import           Network.Haskoin.Wallet.Transaction
import           Network.Haskoin.Wallet.Types

mainChain :: (MonadIO m, MonadThrow m)
          => Either BlockHeight BlockHash
          -> ListRequest
          -> SqlPersistT m (ListResult NodeBlock)
mainChain blockE ListRequest{..} = do
    bestHash <- fst <$> walletBestBlock
    bestM <- getBlockByHash bestHash
    best <- maybe (throwM $ WalletException "Could not find wallet best block")
        return bestM
    remoteNode <- case blockE of
        Right h -> do
            remoteNodeM <- getBlockByHash h
            maybe (throwM $ WalletException "Colud not get remote node")
                return remoteNodeM
        Left h -> do
            heightNodeM <- getBlockByHeight best h
            maybe (throwM $ WalletException "Could not find bock height")
                return heightNodeM
    frst <- (+1) . nodeBlockHeight <$> splitBlock best remoteNode
    if nodeBlockHeight best < frst
        then return $ ListResult [] 0
        else do
            let cnt = nodeBlockHeight best - frst
                limit = min listLimit (cnt - listOffset)
                offset =
                    if listReverse
                    then cnt - listOffset - limit
                    else listOffset
            nodes <- getBlocksFromHeight best limit (frst + offset)
            return $ ListResult nodes cnt

blockTxs :: [NodeBlock] -> [WalletTx] -> [(NodeBlock, [WalletTx])]
blockTxs blocks transactions = reverse $ go [] blocks transactions
  where
    go bs [] _ = bs
    go bs (n:ns) [] = go ((n,[]):bs) ns []
    go [] (n:ns) xs = go [(n,[])] ns xs
    go (b:bs) (n:ns) (x:xs)
       | nodeHash (fst b) == blockHashOf x =
           go ((fst b, x : snd b) : bs) (n:ns) xs
       | nodeHash n == blockHashOf x =
           go ((n, [x]) : b : bs) ns xs
       | otherwise = go ((n, []) : b : bs) ns (x:xs)
    blockHashOf t = fromMaybe
        (throw $ WalletException "Unexpected unconfirmed transaction")
        (walletTxConfirmedBy t)