using System;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Services.SystemSettings;
using Valg.EVA.Skanning.ServicesModel.Internal.BallotNumber;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.Verification.Read;
using Valg.EVA.Skanning.UserInterfaceElements.LightBox;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.DataProvider;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.Model;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Prism.Commands;
using Valg.EVA.Skanning.UserInterfaceElements;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.BallotNumber;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Header;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.VerifiableViewModelsCollection;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Messaging.Commands;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Messaging.Events;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.WriteModel;

namespace Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Header
{
    public class AffiliationHeaderViewModel : ViewModelBase, IAffiliationHeaderViewModel
    {
        private readonly ICandidateListDataProvider provider;
        private readonly IWriteModelHub writeModelHub;
        private readonly IElectionConfigDataProvider electionConfigDataProvider;
        private readonly ISystemSettingsService systemSettings;
        private List<AffiliationSelection> affiliationsToSelectFrom;
        private BallotNumberReadOnly model;

        private bool needsVerification;
        private bool neededVerification;
        private AffiliationSelection selectedAffiliation;
        private CheckForCorrectionsSelection checkForCorrections;
        private RejectionReasonType rejectionReason = RejectionReasonType.NotRejected;
        private IOrderedEnumerable<AffiliationSelection> actualAffiliationsRunningInCurrentElection;
        private Contest currentContest;
        private string electionEventId;
        private string electionGroupId;
        private string electionId;
        private string contestId;
        private Election currentElection;
        private bool isDropDownOpen;
        private bool checkForCorrectionsNeverAvailable;
        private bool ballotTypeIsGeneral;

        public AffiliationHeaderViewModel(ICandidateListDataProvider provider,
            IWriteModelHub writeModelHub,
            IElectionConfigDataProvider electionConfigDataProvider,
            ISystemSettingsService systemSettings)
        {
            this.provider = provider;
            this.writeModelHub = writeModelHub;
            this.electionConfigDataProvider = electionConfigDataProvider;
            this.systemSettings = systemSettings;

            ShowContestAffiliationLightBoxCommand = new DelegateCommand(ShowElectionContestAffiliationInfoLightBox);
            ShowCheckForCorrectionsLightBoxCommand = new DelegateCommand(ShowCheckForCorrectionsLightBox);
            ContinueCommand = new DelegateCommand(() => InputIsCompleteAndVerificationCanContinue());

            affiliationsToSelectFrom = new List<AffiliationSelection>();
        }

        protected override void SubscribeToViewModelSpecificEvents()
        {
            SubscribeToEvent<RejectionReasonChangedEvent, RejectionReasonChangedEventArgs>(RejectionReasonChangedEventHandler);
            SubscribeToEvent<AffiliationSelectionChangedEvent, AffiliationSelectionChangedEventArgs>((arg) =>
            {
                if (arg.Sender != this)
                {
                    if (SelectedAffiliation != null && !SelectedAffiliation.Name.Equals(arg.Selection.Name))
                    {
                        SelectedAffiliation = AffiliationsToSelectFrom.FirstOrDefault(p => p.Name == arg.Selection.Name);
                        OnPropertyChanged(nameof(SelectedAffiliation));
                    }
                }
            });
            SubscribeToEvent<CheckForCorrectionsSelectionEvent, CheckForCorrectionsSelectionEventArgs>((args) =>
            {
                CheckForCorrections = args.CheckForCorrectionsSelection;
            });
        }

        private void RejectionReasonChangedEventHandler(RejectionReasonChangedEventArgs args)
        {
            if (args.CurrentRejectionReason == RejectionReasonType.NoOfficialStamp)
            {
                Reset();
            }
        }

        public IReadOnlyCollection<AffiliationNameReadOnly> affiliationNames { get; set; }

        public DelegateCommand ContinueCommand { get; set; }

        public int VerificationOrder { get; set; }

        public int PageIndex { get; set; }

        public ICommand ShowContestAffiliationLightBoxCommand { get; private set; }

        public ICommand ShowCheckForCorrectionsLightBoxCommand { get; private set; }

        public void Reset()
        {
            if (NeededVerification)
            {
                NeedsVerification = true;
                RejectionReason = RejectionReasonType.NotRejected;
                SelectedAffiliation = AffiliationSelection.NoAffiliationSelectedPlaceholder;

                OnPropertyChanged(null);
            }
        }

        private void InputIsCompleteAndVerificationCanContinue()
        {
            NeedsVerification = false;

            if (RejectionReasonsCollections.BallotNumberRejectionReasons.Contains(RejectionReason))
            {
                writeModelHub.SetRejectionReason(RejectionReason, PageIndex);
            }
            else
            {
                writeModelHub.SetNotRejected(RejectionReasonsCollections.BallotNumberRejectionReasons);

                var ignoreCandidates = CheckForCorrections == CheckForCorrectionsSelection.No;
                var ballotNumberModification = new BallotNumberModification(
                    electionEventId,
                    electionGroupId,
                    electionId,
                    contestId,
                    SelectedAffiliation.AffiliationCode,
                    ignoreCandidates);

                writeModelHub.AddBallotNumberModification(ballotNumberModification);
                
                foreach (var affiliationName in affiliationNames)
                {
                    var affiliationNameModification = new AffiliationNameModification(true,affiliationName.FieldIndex);
                    writeModelHub.AddAffiliationNameModification(affiliationNameModification);
                }
            }

            PublishEvent<VerificationResolvedOnVerifiableViewModelEvent, VerificationWasResolvedEventArgs>(
                new VerificationWasResolvedEventArgs(this, NeededVerification, true));
        }

        public IEnumerable<IViewModelWithVerifiableContent> GetVerificationFields()
        {
            return new[] { this };
        }

        public void RemainFocusAtThisViewModel()
        {
            BringToFocus();
        }

        public void BringToFocus()
        {
            IsDropDownOpen = true;
        }

        public void SetModel(BallotNumberReadOnly ballotNumberReadOnlyModel,
            string currentElectionEventId, string currentElectionGroupId, string currentElectionId, string currentContestId,
            bool isCurrentlyCountedByTopLevelCountingAuthority, RejectionReasonType reason, bool isGeneralBallot, IReadOnlyCollection<AffiliationNameReadOnly> affiliationNames)
        {
            model = ballotNumberReadOnlyModel;
            this.affiliationNames = affiliationNames;
            electionEventId = currentElectionEventId;
            electionGroupId = currentElectionGroupId;
            electionId = currentElectionId;
            contestId = currentContestId;

            ballotTypeIsGeneral = isGeneralBallot;

            var additionalElectionConfiguration = systemSettings.GetAdditionalElectionConfiguration(currentElectionEventId, currentElectionId, currentElectionGroupId, currentContestId);
            checkForCorrectionsNeverAvailable = !isCurrentlyCountedByTopLevelCountingAuthority && additionalElectionConfiguration.AlsoCountedOnLowerLevels;

            NeededVerification = affiliationNames.Any(p => p.NeedsVerification);

            var reasonIsValidForBallotNumber = RejectionReasonsCollections.BallotNumberRejectionReasons.Contains(reason);
            
            NeedsVerification = affiliationNames.Any(p => p.NeedsVerification);

            RejectionReason = reasonIsValidForBallotNumber ? reason : RejectionReasonType.NotRejected;

            SetInitialValues();

            currentContest = electionConfigDataProvider.GetContest(electionEventId, contestId);
            currentElection = electionConfigDataProvider.GetElection(electionEventId, electionId);
        }

        private void LoadAffiliations()
        {
            if (AffiliationsToSelectFrom != null && AffiliationsToSelectFrom.Count == 0)
            {
                var affiliations = provider.GetAffiliations(electionEventId, contestId);
                if (affiliations.Empty())
                {
                    AffiliationsToSelectFrom = new List<AffiliationSelection> { AffiliationSelection.NoAffiliationSelectedPlaceholder };
                }
                else
                {
                    var blank = affiliations.SingleOrDefault(a => a.AffiliationId == Affiliation.Blank.AffiliationId) ?? Affiliation.Blank;
                    affiliations.Remove(blank);

                    actualAffiliationsRunningInCurrentElection = affiliations
                        .Select(a => new AffiliationSelection(a))
                        .OrderBy(s => s.Name);

                    var selections = new List<AffiliationSelection>(actualAffiliationsRunningInCurrentElection);
                    selections.Insert(0, new AffiliationSelection(blank));
                    selections.Insert(0, AffiliationSelection.NoAffiliationSelectedPlaceholder);
                    AffiliationsToSelectFrom = selections;
                }
            }
        }

        private void SetInitialValues()
        {
            LoadAffiliations();

            if (model == null || model.NeedsVerification)
            {
                if (model != null && ShortCodeInBallotNumberIsBlankBallot(model.CurrentValue))
                {
                    SelectedAffiliation = AffiliationsToSelectFrom[1]; 
                }
                else
                {
                    SelectedAffiliation = AffiliationsToSelectFrom.First();
                }
            }
            else
            {
                if (RejectionReason != RejectionReasonType.NoOfficialStamp)
                {
                    var isRejectedBasedOnBallotNumber = RejectionReason == RejectionReasonType.UnknownElection
                                                    || RejectionReason == RejectionReasonType.UnknownPartyOrGroup
                                                    || RejectionReason == RejectionReasonType.PartyOrGroupDoesNotApply;

                if (ShortCodeInBallotNumberIsBlankBallot(model.CurrentValue))
                {

                    SelectedAffiliation = AffiliationsToSelectFrom[1];
                }

                if (!isRejectedBasedOnBallotNumber)
                {
                    if (!NeedsVerification && !model.NeedsVerification)
                    {
                        var affiliationShortCode = BallotNumberSplitter.GetAffiliationShortCode(model.CurrentValue);
                        var firstMatchingAffiliation = AffiliationsToSelectFrom.FirstOrDefault(p => p.AffiliationCode == affiliationShortCode);

                        if (firstMatchingAffiliation != null)
                        {
                            SelectedAffiliation = firstMatchingAffiliation;
                        }
                    }
                }
                }
            }
        }

        public string BallotNumberHeaderText
        {
            get
            {
                if (NeedsVerification)
                {
                    return Resources.Language.VerifyLanguageString.AffiliationName_HeaderText_NeedsVerification;
                }

                return Resources.Language.VerifyLanguageString.AffiliationName_HeaderText;
            }
        }

        private bool ShortCodeInBallotNumberIsBlankBallot(string ballotNumber)
        {
            try
            {
                return Affiliation.IsProposedShortCodeBlankAffiliation(BallotNumberSplitter.GetAffiliationShortCode(ballotNumber));
            }
            catch (Exception)
            {
                return false;
            }
        }

        public bool IsContinueButtonEnabled
        {
            get
            {
                var isRejected = RejectionReason == RejectionReasonType.UnknownElection
                    || RejectionReason == RejectionReasonType.UnknownPartyOrGroup
                    || RejectionReason == RejectionReasonType.PartyOrGroupDoesNotApply;

                return isRejected || (!SelectedAffiliation.IsNoAffiliationSelected);
            }
        }



        public List<AffiliationSelection> AffiliationsToSelectFrom
        {
            get { return affiliationsToSelectFrom; }
            set
            {
                affiliationsToSelectFrom = value;
                OnPropertyChanged();
            }
        }

        public AffiliationSelection SelectedAffiliation
        {
            get { return selectedAffiliation; }
            set
            {
                selectedAffiliation = value ?? AffiliationSelection.NoAffiliationSelectedPlaceholder;


                if (!selectedAffiliation.IsNoAffiliationSelected)
                {
                    RejectionReason = RejectionReasonType.NotRejected;
                }

                OnPropertyChanged();

                OnPropertyChanged(PropertyHelper.GetName<BallotNumberViewModel>(v => v.CheckForCorrectionsVisible));
                OnPropertyChanged(PropertyHelper.GetName<BallotNumberViewModel>(v => v.IsContinueButtonEnabled));

                PublishEvent<AffiliationSelectionChangedEvent, AffiliationSelectionChangedEventArgs>(new AffiliationSelectionChangedEventArgs() { Selection = SelectedAffiliation, Sender = this });
            }
        }

        public CheckForCorrectionsSelection CheckForCorrections
        {
            get { return checkForCorrections; }
            set
            {
                checkForCorrections = value;
                OnPropertyChanged();
                OnPropertyChanged(nameof(IsContinueButtonEnabled));
            }
        }

        public RejectionReasonType RejectionReason
        {
            get { return rejectionReason; }
            set
            {
                rejectionReason = value;

                if (rejectionReason != RejectionReasonType.NotRejected)
                {
                    SelectedAffiliation = AffiliationSelection.NoAffiliationSelectedPlaceholder;
                }

                OnPropertyChanged();
                OnPropertyChanged(nameof(IsContinueButtonEnabled));
            }
        }


        private void ShowElectionContestAffiliationInfoLightBox()
        {
            var dataContext = new ElectionContestAffiliationInfoDialogViewModel(currentContest.Name, currentElection.Name, actualAffiliationsRunningInCurrentElection.ToArray());
            var eventArgs = new LightBoxEventArgs(typeof(ContestAffiliationWindow), dataContext);
            PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(eventArgs);
        }

        private void ShowCheckForCorrectionsLightBox()
        {
            var eventArgs = new LightBoxEventArgs(typeof(CheckForCorrectionsWindow), new CheckForCorrectionsViewModel());
            PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(eventArgs);
        }

        public bool NeedsVerification
        {
            get { return needsVerification; }
            set
            {
                if (needsVerification == value)
                    return;

                needsVerification = value;
                OnPropertyChanged();
                OnPropertyChanged(nameof(BallotNumberHeaderText));
            }
        }

        public bool VerifiedOk => !NeedsVerification;

        public bool NeededVerification
        {
            get { return neededVerification; }
            set
            {
                if (needsVerification == value)
                    return;

                neededVerification = value;
                OnPropertyChanged();
            }
        }

        public bool IsDropDownOpen
        {
            get { return isDropDownOpen; }
            set
            {
                isDropDownOpen = value;
                OnPropertyChanged();
            }
        }
    }
}
