using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Utilities;
using Transaction = DBreeze.Transactions.Transaction;

namespace Stratis.Bitcoin.Features.Consensus.CoinViews
{
    /// <inheritdoc cref="DBreezeCoinView" />
    public class DeStreamDBreezeCoinView : DBreezeCoinView, IDisposable
    {
        public DeStreamDBreezeCoinView(Network network, DataFolder dataFolder, IDateTimeProvider dateTimeProvider,
            ILoggerFactory loggerFactory, INodeStats nodeStats)
            : base(network, dataFolder, dateTimeProvider,
            loggerFactory, nodeStats)
        {
        }

        public DeStreamDBreezeCoinView(Network network, string folder, IDateTimeProvider dateTimeProvider,
            ILoggerFactory loggerFactory, INodeStats nodeStats)
            : base(network, folder, dateTimeProvider, loggerFactory,
            nodeStats)
        {
        }

        /// <inheritdoc />
        // Instead of ignoring genesis coins, add them to database
        public override Task InitializeAsync()
        {
            var genesis = this.network.GetGenesis();

            var task = Task.Run(() =>
            {
                using (Transaction transaction = CreateTransaction())
                {
                    transaction.ValuesLazyLoadingIsOn = false;
                    transaction.SynchronizeTables("BlockHash");

                    if (this.GetTipHash(transaction) != null) return;
                    this.SetBlockHash(transaction, genesis.GetHash());

                    // Genesis coin is spendable and added to the database.
                    foreach (UnspentOutputs unspentOutput in
                        genesis.Transactions.Select(p => new UnspentOutputs(0, p)))
                    {
                        transaction.Insert("Coins", unspentOutput.TransactionId.ToBytes(false),
                            unspentOutput.ToCoins());
                    }

                    transaction.Commit();
                }
            });

            return task;
        }
    }
}