using PhilExampleCrawler.Common.Models;
using WebAPI.Interfaces;
using TL;
using PhExUser = PhilExampleCrawler.Common.Models.User;
using TLUser = TL.User;
using PhilExampleCrawler.DataBase;
using PhilExampleCrawler.DataBase.Models;
using TL.Methods;

namespace WebAPI.Services
{
    public class TelegramDistributer : ITelegramDistributer
    {
        private readonly ICrawlScheduler<List<Insertion>> _scheduler;
        private readonly WTelegram.Client _tClient;
        private readonly UserAccess _userDB = new(new DBSettings());

        ///// <summary> Represents Telegram User ID, PhilExampleUser </summary>
        /// <summary> Represents (Phone, Telegram UserID) </summary>
        private readonly List<(PhExUser PhilExampleUser, TLUser telegramUser)> _users = new();

        public TelegramDistributer(ICrawlScheduler<List<Insertion>> scheduler)
        {
            _scheduler = scheduler;

            _tClient = new WTelegram.Client(apiID: 1234,
                                            apiHash: "1234567890ABCDEFGHIJKLMNOP");
        }

        public Task SendAsync(PhExUser user, List<Insertion> insertions)
        {
            return Task.CompletedTask;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            return Task.CompletedTask;
        }

        /// <summary> Imports PhilExample Users' phone numbers to telegramClient contacts 
        /// and caches their telegram user id to message them later on </summary>
        public async Task UpdateUserListAsync()
        {
            List<PhExUser> phExUsers = (await _userDB.GetUsersAsync())
                                                 .Where(x => x.Optin_Telegram)
                                                 .Select(x => x.ToCommon())
                                                 .ToList() ?? new();

            Contacts_ImportedContacts tImport = await _tClient.Contacts_ImportContacts(phExUsers.Select(x => new InputPhoneContact() { phone = x.Phone }).ToArray());
            Dictionary<long, TLUser> importedUsers = tImport.users.Where(u => tImport.imported.Any(imp => imp.user_id == u.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            foreach(var teleUser in importedUsers)
            {
                var phExUser = phExUsers.FirstOrDefault(u => Validator.PhoneNumber_Normalize(u.Phone) == 
                                                         Validator.PhoneNumber_Normalize(teleUser.Value.phone));
                if(phExUser != null)
                {
                    //user.HasTelegram = true;
                    //_contacts.Add(tUser.Key, user);
                    _users.Add(new(phExUser, teleUser.Value));
                }
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            Console.WriteLine("TelegramDistributer stopped");
            return Task.CompletedTask;
        }

        private async void Scheduler_OnResult(object? sender, List<Insertion> insertions)
        {
            if (_tClient.User == null ||
                insertions == null ||
                insertions.Count == 0 ||
                _users.Count == 0)
                return;

            //Grouping belonging insertions to telegramUser based on users CrawlSessions
            Dictionary<TLUser, List<Insertion>> user_Insertions = new();
            foreach(var i in insertions)
            {
                var (PhilExampleUser, telegramUser) = _users.FirstOrDefault(u => u.PhilExampleUser.CrawlSessions.Any(cs => cs.ID == i.CrawlSessionID));
                if (PhilExampleUser == null || telegramUser == null)
                    continue;

                if (user_Insertions.ContainsKey(telegramUser))
                    user_Insertions[telegramUser].Add(i);
                else
                    user_Insertions.Add(telegramUser, new List<Insertion>() { i });
            }

            foreach(var userIs in user_Insertions)
            {
                string noti = CreateNotification(userIs.Value);
                var entities = _tClient.HtmlToEntities(ref noti);
                var sent = await _tClient.SendMessageAsync(userIs.Key, noti, entities: entities);
            }

            ThreadSafeCache.AddInsertionsAsync(insertions);
        }

        bool initialized = false;
        public async Task<string> RefetchCredentialsAsync(string creds)
        {
            if (_tClient.User == null)
            {
                return await _tClient.Login(creds);
            }
            else if (!initialized)
            {
                //_tClient.OnUpdate += TClient_OnUpdate;
                await UpdateUserListAsync();
                initialized = true;
                _scheduler.OnResult += Scheduler_OnResult;
                return "Client was initialized.";
            }
            else
                return "Client already initialized.";
        }

        private async Task TClient_OnUpdate(IObject arg)
        {
            if (arg is not UpdatesBase updates) 
                    return;
            else
            {
                var asd = updates;
            }
        }

        private static string CreateNotification(List<Insertion> userIs)
        {
            string noti = "";
            if (userIs.Count == 1)
                noti = $"<u><b>Neues PhilExample Suchergebnis:</b></u>";
            else if (userIs.Count > 1)
                noti = $"<u><b>Neue PhilExample Suchergebnisse:</b></u>";

            foreach (var i in userIs)
            {
                noti += $"\n<a href=\"{"https://examplepage.com" + i.Href}\">{i.Name}</a>";

                if (i.Price == 0)
                    noti += $"\n <i>gratis</i>";
                else
                    noti += $"\n <i>{i.Price} € {(i.Is_VB ? "VB" : null)}</i>";

                noti += $"\n";
            }

            return noti;
        }

    }
}
