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

namespace HyperCube.Models
{
    public enum Role { Admin = 1, Verifier, Initiator, Requester }

    public class AccountModel
    {
        //public event EventHandler<int> RolesChanged;
        public Action<int> RolesChanged;
        public byte bsel;
        public byte blockchain_selected
        {
            get { return bsel; }
            set {               
                bsel = value;
                bcselupdate();
            }
        }
        public List<Role> Roles = new();

        public void AddRole(Role role)
        {
            Roles.Add(role);
            RolesChanged?.Invoke(Roles.Count);
            Console.WriteLine($"role added, count: {Roles.Count}");
        }

        async Task bcselupdate()
        {
            MySQLConnector.Instance().SQLInsert($"update aspnetusers set blockchain_selected = {blockchain_selected} where Id='{UUID}'");
        }

        public void RemoveRole(Role role)
        {
            Roles.Remove(role);
            RolesChanged?.Invoke(Roles.Count);
            Console.WriteLine($"role removed, сount: {Roles.Count}");
        }

        public static Dictionary<string, AccountModel> Loaded = new();
        public static AccountModel Current;
        string eth_address { get; set; }
        string eth_address1 { get; set; }
        /// <summary>
        /// ASP Identity ID
        /// </summary>
        public string UUID { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string PWDHash { get; set; }
        public Role AccRole { get; set; }

        public string GetActualAddress(Blockchain bc)
        {
            string addr;
            if (bc.port == 8666)
                addr = eth_address1;
            else
                addr = eth_address;

            return addr;
        }

        public async Task<string> GetOrCreateActualAddress(Blockchain bc)
        {
            string addr;
            if (bc.port == 8666)
                addr = eth_address1;
            else
                addr = eth_address;

            if (addr == null || addr == "")
                addr = await bc.CreateBlockchainAccount(this, PWDHash);
            return addr;
        }

        public void SetActualAddress(string value, Blockchain bc)
        {
            if (bc.port == 8666)
                eth_address1 = value;
            else
                eth_address = value;
        }

        public AccountModel()
        {
        }

        public static AccountModel GetCurrent()
        {
            return Current;
        }

        public static AccountModel Find(string uuid)
        {
            if (uuid == null)
                return null;
            if (Loaded.ContainsKey(uuid))
                return Loaded[uuid];
            else
                return null;
        }

        public static BigInteger ConvertBalance(string hex)
        {
            if (hex != null && hex.Length > 0)
            {
                Console.WriteLine($"ConvertBalance {hex}");
                string newHex = hex.Remove(0, 2);
                var balance = System.Numerics.BigInteger.Parse("0" + newHex, System.Globalization.NumberStyles.HexNumber);
                Console.WriteLine($"ConvertBalance {hex} {balance}");
                return balance;
            }
            return 0;
        }

        public static async void InitializeAccounts()
        {
            Loaded = await MySQLConnector.Instance().SQLSelectASPUsers();
            Console.WriteLine("InitializeAccounts");
            foreach (var acc in Loaded)
            {
                acc.Value.LoadRoles();
                var wallets = await MySQLConnector.Instance().SQLSelectComplex($"select * from account_wallets where account_uuid='{acc.Value.UUID}'");

                if (wallets.Count > 0)
                {
                    foreach (var wallet in wallets)
                    {
                        var bc_id = Convert.ToInt32(wallet["blockchain_id"]);
                        var wallet_id = Convert.ToString(wallet["uuid"]);
                        if (bc_id == 0)
                            acc.Value.eth_address = wallet_id;
                        else
                            acc.Value.eth_address1 = wallet_id;
                        Console.WriteLine($"acc {acc.Value.Name} wallet0 {acc.Value.eth_address} wallet1 {acc.Value.eth_address1}");
                    }
                }

            }
        }

        public async void LoadRoles()
        {
            var rolesSQL = await MySQLConnector.Instance().SQLSelectComplex($"select * from account_roles where account_uuid ='{this.UUID}'");
            if (rolesSQL.Count > 0)
            {
                foreach (var role in rolesSQL)
                {
                    var role_id = Convert.ToUInt32(role["role_id"]);
                    AccRole = (Role) role_id;
                    if (!Roles.Contains(AccRole))
                    {
                        Console.WriteLine($"LoadRoles uuid {UUID} roleid {role_id} AccRole {AccRole}");
                        AddRole(AccRole);
                    }

                    //functionId = Convert.ToInt32(role["id"]);
                    //functionName = role["name_with_args"].ToString();
                    //functionCompiledHeader = role["compiled_header"].ToString();
                    //Console.WriteLine($"{ID} LoadFunction {functionId} name {functionName} header {functionCompiledHeader}");
                }
            }            
        }

        //public async Task CreateEthAddress(string pass)
        //{
        //    var addr = await Blockchain.GetMain().CreateBlockchainAccount(this, pass);
        //}

        public async Task<string> GetBalance()
        {
            var res = await Blockchain.GetMain().GetBalance(this);
            var balanceInt = ConvertBalance(res);
            string balance = balanceInt.ToString();
            //Console.WriteLine($"GetBalance {balance}");
            return balance;
        }
    }
}