using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using PhilExample.Interfaces;
using Xamarin.Forms;
using System.Linq;
using PhilExample.UI.Pages;
using PhilExample.Utils;
using PhilExampleCrawler.Common.Models;
using System.Collections.ObjectModel;
using PhilExample.Models;

namespace PhilExample.VMs
{
    public class DashboardVM : BaseVM
    {
        private readonly IUserService _userService = DependencyService.Get<IUserService>();
        private readonly ICrawlSessionService _crawlService = DependencyService.Get<ICrawlSessionService>();

        public ICommand AddCommand { get; set; }
        public ICommand EditCommand { get; set; }
        public ICommand RefreshInsertionsCommand { get; }

        private ObservableCollection<CrawlSessionMiniVM> _crawlSessions = new ObservableCollection<CrawlSessionMiniVM>();
        private ObservableCollection<InsertionMiniVM> _foundInsertions = new ObservableCollection<InsertionMiniVM>();
        private bool _isRefreshing;

        public ObservableCollection<CrawlSessionMiniVM> CrawlSessions { get => _crawlSessions; set => SetProperty(ref _crawlSessions, value); }
        public ObservableCollection<InsertionMiniVM> FoundInsertions { get => _foundInsertions; set => SetProperty(ref _foundInsertions, value); }
        public bool IsRefreshing { get => _isRefreshing; set => SetProperty(ref _isRefreshing, value); }


        public DashboardVM()
        {
            AddCommand = new Command(async () => await AddCrawlSessionAsync());
            EditCommand = new Command(async (csID) => await EditCrawlSessionAsync((int)csID));
            RefreshInsertionsCommand = new Command(async () => await UpdateInsertionsAsync());
            SetCrawlSessions();
        }

        private async Task UpdateInsertionsAsync()
        {
            if (IsRefreshing)
                return;
            IsRefreshing = true;

            if (FoundInsertions == null)
                FoundInsertions = new ObservableCollection<InsertionMiniVM>();
            else
                FoundInsertions.Clear();

            //TODO: Get insertions
            var latestIns = await _crawlService.GetLatestInsertionAsync(_userService.CurrentUser.ID, 5);

            if (latestIns != null)
                foreach (var i in latestIns)
                    if (i != null)
                    {
                        FoundInsertions.Add(new InsertionMiniVM(i));
                        UpdateUI();
                    }

            IsRefreshing = false;
        }

        public override async Task OnAppearingAsync()
        {
            await UpdateInsertionsAsync();
        }

        private async Task AddCrawlSessionAsync()
        {
            await Shell.Current.GoToAsync($"{nameof(AddCrawlSessionPage)}");

            var page = ShellExtensions.GetCurrentPage<AddCrawlSessionPage>();
            if (page != null)
            {
                CrawlSessionResponse csResp = await page.PagePoppedTask;
                if (csResp.Success)
                {
                    _userService.CurrentUser.CrawlSessions.Add(csResp.CrawlSession);
                    CrawlSessions.Add(new CrawlSessionMiniVM(csResp.CrawlSession, EditCommand, csResp.CrawlSession.ID));
                    UpdateUI();
                }
            }
        }

        private async Task EditCrawlSessionAsync(int csID)
        {
            await Shell.Current.GoToAsync($"{nameof(AddCrawlSessionPage)}?{nameof(AddCrawlSessionVM.CrawlSessionID)}={csID}");

            var page = ShellExtensions.GetCurrentPage<AddCrawlSessionPage>();
            if (page == null)
                return;

            CrawlSessionResponse csResp = await page.PagePoppedTask;
            if (!csResp.Success)
                return;

            if(csResp.CrawlSession == null) //deleted
            {
                int uCSIndex = GetIndex(_userService.CurrentUser.CrawlSessions, csID);
                if (uCSIndex > -1)
                    _userService.CurrentUser.CrawlSessions.RemoveAt(uCSIndex);
                int dashboardCSIndex = GetIndex(CrawlSessions, csID);
                if (dashboardCSIndex > -1)
                {
                    CrawlSessions.RemoveAt(dashboardCSIndex);
                    UpdateUI();
                }
            }
            else //updated
            {
                var uCS = _userService.CurrentUser.CrawlSessions.FirstOrDefault(x => x.ID == csResp.CrawlSession.ID);
                var dashboardCS = CrawlSessions.FirstOrDefault(x => (int)x.ActionCommandParameter == csResp.CrawlSession.ID);
                UpdateValues(uCS, csResp.CrawlSession);
                UpdateValues(dashboardCS, csResp.CrawlSession);
            }
        }

        private void UpdateValues(CrawlSession oldCS, CrawlSession newCS)
        {
            if (oldCS == null || newCS == null)
                return;

            oldCS.SearchParams = newCS.SearchParams;
            oldCS.MinPrice = newCS.MinPrice;
            oldCS.MaxPrice = newCS.MaxPrice;
            oldCS.IsPrivate = newCS.IsPrivate;
            oldCS.IsCommercial = newCS.IsCommercial;
        }

        private void UpdateValues(CrawlSessionMiniVM oldCS, CrawlSession newCS)
        {
            if (oldCS != null && newCS != null)
                oldCS.Title = newCS.SearchParams.KeyWords;
        }

        private int GetIndex(List<CrawlSession> l, int csID)
        {
            for (int i = 0; i < l.Count; i++)
                if (l[i].ID == csID)
                    return i;

            return -1;
        }

        private int GetIndex(ObservableCollection<CrawlSessionMiniVM> l, int csID)
        {
            for (int i = 0; i < l.Count; i++)
                if ((int)l[i].ActionCommandParameter == csID)
                    return i;

            return -1;
        }

        private void SetCrawlSessions()
        {
            CrawlSessions.Clear();
            foreach (var cs in _userService.CurrentUser.CrawlSessions)
                CrawlSessions.Add(new CrawlSessionMiniVM(cs, EditCommand, cs.ID));
        }

        private void UpdateUI()
        {
            //Important to call when CrawlSessions is getting empty or getting its first element
            //the IsEmpty/IsNotEmpty-IsVisible Property on the views is not triggered from removing/adding to list
            OnPropertyChanged(nameof(CrawlSessions));
            OnPropertyChanged(nameof(FoundInsertions));
        }
    }
}
