using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Numerics;
using Console = HyperCube.Utils.AdvConsole;

namespace HyperCube.Models
{
    public class SmartContract
    {
        public int ID;
        public int blockchainId;
        public string Code;
        public string ByteCode;
        public string Name = "";
        public string Gas = "";
        public string Address = "";
        public int functionId = 0;
        public string functionName = "";
        public string functionCompiledHeader = "";

        public static SmartContract Find(string name, Blockchain bc)
        {
            if (bc != null)
            {
                var contracts = bc.contractNames;
                if (contracts.ContainsKey(name))
                {
                    return contracts[name];
                }
            }
            return null;
        }

        public static async Task<string> Verify(AccountModel verifier, ArticleModel articleModel, Blockchain bc)
        {
            if (articleModel == null)
                return "Verify failed";
            Console.WriteLine($"Verify starting");
            try
            {
                VerifyContract verifyContract = Find("Verify", bc) as VerifyContract;
                if (verifyContract != null)
                {
                    Console.WriteLine($"VerifyContract found");
                    return await verifyContract.Run(verifier, articleModel);
                }
                else
                    Console.WriteLine($"VerifyContract null");

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message + "stack trace" + e.StackTrace);
            }
            return "Verify failed";
        }

        public SmartContract()
        { }

        public async Task<string> Run(AccountModel initiator, string weiValue = "0x0")
        {
            var bc = Blockchain.Find(blockchainId);
            if (bc != null)
            {
                var initiator_address = await initiator.GetOrCreateActualAddress(bc);
                initiator_address = Blockchain.zerofill(initiator_address, 64, true);

                //Console.WriteLine($"VerifyContract params article_value  {article_value} edits {edits} verifier {AccountModel.Current.Name} verifier_address: {verifier_address} initiator {initiator.Name} initiator_address: {initiator_address}");
                //function verify( address payable verifier, address payable initiator, int article_value, int verify_complexity) public returns (uint vReward, uint iReward) 
                var paramstring = "0x" + functionCompiledHeader;
                //Console.WriteLine($"VerifyContract Run {initiator.Name} params {paramstring}");
                var res = await bc.RunContractWrite(Address, $"{paramstring}", Gas, "0x100");
                //Console.WriteLine($"VerifyContract transactionId res {res}");
                var receipt = await bc.GetReceipt(res);
                //Console.WriteLine("VerifyContract receipt " + receipt);
                return res;
            }
            return "none";
        }

        public SmartContract(string name, string code, string bytecode, int blockchain_id, string gas)
        {            
            Name = name;
            Code = code;
            ByteCode = bytecode;
            blockchainId = blockchain_id;
            Gas = gas;
        }

        public async Task LoadFunction()
        {
            if (functionId == 0)
            {
                var ret = await MySQLConnector.Instance().SQLSelectComplex($"select * from functions where contract_id={ID} limit 1");
                if (ret.Count > 0)
                {
                    functionId = Convert.ToInt32(ret[0]["id"]);
                    functionName = ret[0]["name_with_args"].ToString();
                    functionCompiledHeader = ret[0]["compiled_header"].ToString();
                    Console.WriteLine($"{ID} LoadFunction {functionId} name {functionName} header {functionCompiledHeader}");
                }
            }
        }

        public async Task<long> AddFunction(string name_with_args, string compiled_header)
        {
            return await MySQLConnector.Instance().SQLInsert($"insert into functions (name_with_args, contract_id, compiled_header) values ('{name_with_args}',{ID},'{compiled_header}')");
        }
    }    

    public class VerifyContract : SmartContract
    {
        public VerifyContract() : base() { }
        public VerifyContract(string name, string code, string bytecode, int blockchain_id, string gas) : base(name, code, bytecode, blockchain_id, gas) { }        

        /// <summary>
        /// 
        /// </summary>
        /// <param name="newArticle"></param>
        /// <param name="weiValue"></param>
        /// <returns>ID транзакции</returns>
        public async Task<string> Run(AccountModel verifier, ArticleModel newArticle, string weiValue = "0x0")
        {
            var uuidInitiator = await newArticle.GetInitiatorUUID(ArticleStatus.AwatingVerify);
            
            var bc = Blockchain.Find(blockchainId);
            var initiator = AccountModel.Find(uuidInitiator);
            Console.WriteLine($"verify verifierid {verifier.UUID} initiator {uuidInitiator} verifier {verifier.GetActualAddress(bc)} initiator_address {initiator.GetActualAddress(bc)}");
            if (bc != null)
            {
                var verifier_address = await verifier.GetOrCreateActualAddress(bc);

                initiator = AccountModel.Find(uuidInitiator);
                Console.WriteLine($"verify verifier {verifier.UUID} initiator found " + initiator+ " bc port "+ bc.port+ $" initiator_address {initiator.GetActualAddress(bc)}");
                var initiator_address = await initiator.GetOrCreateActualAddress(bc);
                //var article_value = (int)newArticle.Rating;
                var article_value = newArticle.CalcPValue();


                //calc comission

                //init add transaction 2 contract
                //add transaction 2db
                

                var article_value_hex = article_value.ToString("X64");
                //Console.WriteLine($"article_value {article_value} hex {article_value_hex}");
                var edits = await newArticle.GetEditsCount();

                //GetRatingExpert - ???
                //GetRatingProspector - ???
                var expert_value = article_value * verifier.GetRatingExpert() * edits;
                var initiator_value = article_value * initiator.GetRatingProspector();

                Console.WriteLine($"article_value {article_value} expert_value {expert_value} initiator_value {initiator_value} CharCount {newArticle.CharCount} Keywords count {newArticle.Keywords.Split(",").Length} ");

                //init notifications 2DEVS
                return "none";
                var edits_hex = edits.ToString("X64");
                verifier_address = Blockchain.zerofill(verifier_address, 64, true);
                initiator_address = Blockchain.zerofill(initiator_address, 64, true);

                Console.WriteLine($"VerifyContract params article_value  {article_value} edits {edits} verifier {verifier.Name} verifier_address: {verifier_address} initiator {initiator.Name} initiator_address: {initiator_address}");
                //function verify( address payable verifier, address payable initiator, int article_value, int verify_complexity) public returns (uint vReward, uint iReward) 
                var paramstring = "0x" + functionCompiledHeader + verifier_address + initiator_address + article_value_hex + edits_hex;
                Console.WriteLine($"VerifyContract Run {initiator.Name} params {paramstring}");
                var res = await bc.RunContractWrite(Address, $"{paramstring}", Gas, "0x100");
                Console.WriteLine($"VerifyContract transactionId res {res}");
                var receipt = await bc.GetReceipt(res);
                Console.WriteLine("VerifyContract receipt " + receipt);
                return res;
            }
            return "none";
        }
    }
}