using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Valg.EVA.Skanning.ServicesModel.Internal;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.Model;

namespace Valg.EVA.Skanning.ServicesModel.ElectionConfig.DataProvider
{
    public class ElectionConfigDataPreloader
    {
        private readonly IElectionConfigDataProvider electionConfigDataProvider;
        private readonly IMunicipalityDataProvider municipalityDataProvider;
        private readonly ICandidateListDataProvider candidateListDataProvider;
        protected virtual bool RunSyncronously { get { return false; } }

        public ElectionConfigDataPreloader(IElectionConfigDataProvider electionConfigDataProvider, IMunicipalityDataProvider municipalityDataProvider, ICandidateListDataProvider candidateListDataProvider)
        {
            this.electionConfigDataProvider = electionConfigDataProvider;
            this.municipalityDataProvider = municipalityDataProvider;
            this.candidateListDataProvider = candidateListDataProvider;
        }

        public void PreloadData(string electionEventIdForLoadingData)
        {
            var electionEventId = electionEventIdForLoadingData;
            var contestsInElection = GetContestsInElection(electionEventId);

            if (string.IsNullOrEmpty(electionEventId) || !electionEventId.IsStringActuallyLongOrInt() || electionEventId == "-1")
                return;

            var dummyPollingDistrictId = new string(Enumerable.Repeat('1', 18).ToArray());
            var getElectionEventDelegate = new Action(() => electionConfigDataProvider.GetElectionEvent(electionEventId));
            var getContestDelegate = new Action(() => electionConfigDataProvider.GetContest(electionEventId, "1"));
            var getElectionDelegate = new Action(() => electionConfigDataProvider.GetElection(electionEventId, "1"));
            var preloadMunicipalitiesDelegate = new Action(() => municipalityDataProvider.PreLoadCache(electionEventId));
            var getPollingDistrictsDelegate = new Action(() => candidateListDataProvider.GetPollingDistrict(electionEventId, dummyPollingDistrictId));
            var getVotingCategoryDelegate = new Action(() => electionConfigDataProvider.GetVotingCategory(electionEventId, dummyPollingDistrictId, string.Empty));
            var getReportingUnitDelegate = new Action(() => electionConfigDataProvider.GetReportingUnit(electionEventId, dummyPollingDistrictId));
            var getVotingCategoryInContestDelegate = new Action(
                () =>
                {
                    foreach (var contest in contestsInElection)
                    {
                        electionConfigDataProvider.GetVotingCategoriesInContest(electionEventId, contest.Id);
                    }
                });

            if (RunSyncronously)
            {
                getElectionEventDelegate.Invoke();
                getContestDelegate.Invoke();
                getElectionDelegate.Invoke();
                preloadMunicipalitiesDelegate.Invoke();
                getPollingDistrictsDelegate.Invoke();
                getVotingCategoryDelegate.Invoke();
                getReportingUnitDelegate.Invoke();
                getVotingCategoryInContestDelegate.Invoke();
            }
            else
            {
                var loadElectionConfigData =
                    new Task(getElectionEventDelegate.Invoke);
                var loadContest = loadElectionConfigData.ContinueWith(t => getContestDelegate.Invoke());
                var loadElection = loadContest.ContinueWith(t => getElectionDelegate.Invoke());
                var loadMunicipality = loadElection.ContinueWith(t => preloadMunicipalitiesDelegate.Invoke());
                var loadPollingDistrict = loadMunicipality.ContinueWith(t => getPollingDistrictsDelegate.Invoke());
                var loadVotingCategory = loadPollingDistrict.ContinueWith(t => getVotingCategoryDelegate.Invoke());
                var loadVotingCategoryInContest = loadVotingCategory.ContinueWith(t => getVotingCategoryInContestDelegate.Invoke());
                loadVotingCategoryInContest.ContinueWith(t => getReportingUnitDelegate.Invoke());
                loadElectionConfigData.Start();   
            }
        }

        private List<Contest> GetContestsInElection(string electionEventId)
        {
            return electionConfigDataProvider.GetContestsInElection(electionEventId);
        } 
    }
}
