using System;
using System.Linq;
using Trinity.Core;
using Trinity.Extensions;
using Trinity.Network;
using Trinity.Wallets;

namespace Trinity.CLI
{
	public class Coins
	{
		private readonly IBlockchain _blockchain;
		private readonly ILocalNode _localNode;
		private readonly IWallet _wallet;

		public Coins(IWallet wallet, ILocalNode localNode)
		{
			_wallet = wallet;
			_localNode = localNode;
			_blockchain = _localNode.Blockchain;
		}

		public Fixed8 UnavailableBonus()
		{
			var height = _blockchain.Height + 1;
			Fixed8 unavailable;

			try
			{
				unavailable =
					_blockchain.CalculateBonus(
						_wallet.FindUnspentCoins().Where(p => p.Output.AssetId.Equals(Blockchain.UtilityToken.Hash))
							.Select(p => p.Reference), height);
			}
			catch (Exception)
			{
				unavailable = Fixed8.Zero;
			}

			return unavailable;
		}


		public Fixed8 AvailableBonus()
		{
			return _blockchain.CalculateBonus(_wallet.GetUnclaimedCoins().Select(p => p.Reference));
		}


		public ClaimTransaction Claim()
		{
			if (AvailableBonus() == Fixed8.Zero)
			{
				Console.WriteLine($"no gas to claim");
				return null;
			}

			var claims = _wallet.GetUnclaimedCoins().Select(p => p.Reference).ToArray();
			if (claims.Length == 0)
			{
				return null;
			}

			var tx = new ClaimTransaction
			{
				Claims = claims,
				Attributes = new TransactionAttribute[0],
				Inputs = new CoinReference[0],
				Outputs = new[]
				{
					new TransactionOutput
					{
						AssetId = Blockchain.UtilityToken.Hash,
						Value = _blockchain.CalculateBonus(claims),
						ScriptHash = _wallet.GetChangeAddress()
					}
				}
			};

			return (ClaimTransaction) SignTransaction(tx);
		}


		private Transaction SignTransaction(Transaction tx)
		{
			if (tx == null)
			{
				Console.WriteLine($"no transaction specified");
				return null;
			}
			SignatureContext context;

			try
			{
				context = new SignatureContext(tx);
			}
			catch (InvalidOperationException)
			{
				Console.WriteLine($"unsynchronized block");

				return null;
			}

			_wallet.Sign(context);

			if (context.Completed)
			{
				context.Verifiable.Scripts = context.GetScripts();
				_wallet.SaveTransaction(tx);

				var relayResult = _localNode.Relay(tx);

				if (relayResult)
				{
					return tx;
				}
				Console.WriteLine($"Local Node could not relay transaction: {tx.Hash}");
			}
			else
			{
				Console.WriteLine($"Incomplete Signature: {context}");
			}

			return null;
		}
	}
}