using Hcs.WebApp.BackgroundServices;
using Hcs.WebApp.Data.Hcs;
using Hcs.WebApp.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

namespace Hcs.WebApp.Components.Shared
{
    public abstract class SyncedPageBase<TData> : ComponentBase, IDisposable
    {
        protected SyncedPageState state;
        protected IEnumerable<TData> data;
        protected string syncButtonText = "...";

        protected abstract Campaign.CampaignType CampaignType { get; }

        [Inject]
        private AuthenticationStateProvider AuthenticationStateProvider { get; set; }

        [Inject]
        private HeadquartersService HeadquartersService { get; set; }

        [Inject]
        private CampaignManagementState CampaignManagementState { get; set; }

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                ChangeState(SyncedPageState.Init);

                var finalState = SyncedPageState.Idle;
                var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
                if (HasPermission(state.User))
                {
                    var operationInProgress = await HeadquartersService.HasActiveCampaignAsync(CampaignType);
                    if (operationInProgress)
                    {
                        finalState = SyncedPageState.SyncWaiting;
                    }

                    CampaignManagementState.OnCampaignCreated += OnCampaignCreated;
                    CampaignManagementState.OnCampaignEnded += OnCampaignEnded;

                    data = await GetDataAsync();
                }

                ChangeState(finalState);
            }
        }

        public void Dispose()
        {
            CampaignManagementState.OnCampaignCreated -= OnCampaignCreated;
            CampaignManagementState.OnCampaignEnded -= OnCampaignEnded;
        }

        protected abstract bool HasPermission(ClaimsPrincipal user);

        protected abstract Task<IEnumerable<TData>> GetDataAsync();

        protected async Task SyncDataAsync()
        {
            if (state == SyncedPageState.SyncWaiting) return;

            ChangeState(SyncedPageState.SyncWaiting);

            if (await HeadquartersService.HasActiveCampaignAsync(CampaignType))
            {
                ChangeState(SyncedPageState.Idle);
            }
            else
            {
                // TODO: Use user id
                var campaign = await HeadquartersService.InitiateCampaignAsync(CampaignType, "");
                CampaignManagementState.EnqueueCampaign(campaign);
            }
        }

        private void ChangeState(SyncedPageState state)
        {
            if (this.state == state) return;

            this.state = state;

            SetSyncButtonText();
            StateHasChanged();
        }

        private void SetSyncButtonText()
        {
            switch (state)
            {
                case SyncedPageState.Init:
                    syncButtonText = "...";
                    break;

                case SyncedPageState.Loading:
                case SyncedPageState.Idle:
                    syncButtonText = "Синхронизировать";
                    break;

                case SyncedPageState.SyncWaiting:
                    syncButtonText = "Идет синхронизация...";
                    break;
            }
        }

        private void OnCampaignCreated(Campaign campaign)
        {
            if (campaign.Type == CampaignType)
            {
                InvokeAsync(() => ChangeState(SyncedPageState.SyncWaiting));
            }
        }

        private void OnCampaignEnded(int campaignId, Campaign.CampaignType type, DateTime endedAt, string failureReason)
        {
            if (type == CampaignType)
            {
                Task.Run(RefreshData);
            }
        }

        private async Task RefreshData()
        {
            await InvokeAsync(() => ChangeState(SyncedPageState.Loading));

            var refreshedData = await GetDataAsync();

            await InvokeAsync(() =>
            {
                data = refreshedData;

                ChangeState(SyncedPageState.Idle);
            });
        }
    }
}
