using System;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using System.Collections.Generic;
using Console = HyperCube.Utils.AdvConsole;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace HyperCube.Models
{
    public enum Role {
        [Display(Name = "Администратор")]
        Admin = 1,
        [Display(Name = "Эксперт")]
        Expert,
        [Display(Name = "Старатель")]
        Miner,
        [Display(Name = "Заказчик")]
        Customer,
        [Display(Name = "Инвестор")]
        Investor,
        [Display(Name = "Спонсор")]
        Sponsor,
        [Display(Name = "Наблюдатель")]
        Observer
    }

    public class AccountModel: ComponentBase
    {
        [Inject]
        AppData AppData { get; set; }
        [Inject]
        ProtectedSessionStorage storage { get; set; }
        //public event EventHandler<int> RolesChanged;
        int ratingExpert = 1;
        int ratingProspector = 1;
        public int GetRatingExpert()
        {
            return ratingExpert;
        }
        public int GetRatingProspector()
        {
            return ratingProspector;
        }
        public Action<int> RolesChanged;
        public byte bsel = 0;
        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}");
        }

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

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

        public static Dictionary<string, AccountModel> Loaded = new();
        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 List<string> GetRoleDisplayName()
        {
            List<string> _rolesDisplayName = new();
            foreach (Role role in Roles)
            {
                _rolesDisplayName.Add(
                    role.GetType()
                    .GetMember(role.ToString())
                    .First()
                    .GetCustomAttribute<DisplayAttribute>()
                    .GetName()
                );
            }
            return _rolesDisplayName;
        }

        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;

            Console.WriteLine($"got address1 {eth_address1}");
            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 async Task<Blockchain> GetSelectedBlockChain()
        {
            //Console.WriteLine($"AppData null { AppData == null}");
            //var res = await storage.GetAsync<AccountModel>("acc");
            //var accountCurrent = res.Value;
            //var accountCurrent = AppData.CurrentAccount;
            //Console.WriteLine($"AppData acc null { accountCurrent == null}");
            if (Blockchain.loaded.Count > blockchain_selected)
            {
                var bc = Blockchain.loaded[blockchain_selected];
                Console.WriteLine($"GetMain blockchain_selected {blockchain_selected} " + bc.address);
                return bc;
            }
            else
            {
                Console.WriteLine($"Error: blockchains loaded {Blockchain.loaded.Count}");
                blockchain_selected = (byte)(Blockchain.loaded.Count - 1);
            }

            return null;
        }

        public static AccountModel FindByMail(string mail)
        {
            var selectedUsers = from user in Loaded.Values
                                where user.Email == mail
                                select user;
            return selectedUsers.First();
        }

        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 bc = await GetSelectedBlockChain();
            var res = await bc.GetBalance(this);
            //var balanceInt = ConvertBalance(res);
            //string balance = balanceInt.ToString();
            //Console.WriteLine($"GetBalance {balance}");
            return res;
        }
    }
}
