using FluentValidation;
using Valg.EVA.Skanning.Services.SystemSettings;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.DataProvider;
using Valg.EVA.Skanning.ServicesModel.Internal;

namespace Valg.Eva.Skanning.Scan.Presentation.RegisterBarcode.Barcode
{
    public class BarcodeValidator : AbstractValidator<Barcode>, IBarcodeValidator
    {
        private readonly IElectionConfigDataProvider electionConfigDataProvider;
        private readonly IMunicipalityDataProvider municipalityDataProvider;
        private readonly ICandidateListDataProvider candidateListDataProvider;
        private readonly ISystemSettingsService systemSettingsService;

        public BarcodeValidator(IElectionConfigDataProvider electionConfigDataProvider, IMunicipalityDataProvider municipalityDataProvider, ICandidateListDataProvider candidateListDataProvider, 
            ISystemSettingsService systemSettingsService)
        {
            this.electionConfigDataProvider = electionConfigDataProvider;
            this.municipalityDataProvider = municipalityDataProvider;
            this.candidateListDataProvider = candidateListDataProvider;
            this.systemSettingsService = systemSettingsService;

            CascadeMode = CascadeMode.Continue;

            RuleFor(b => b.BarcodeLong).NotEmpty().WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.BarcodeLongCannotBeEmpty);
            RuleFor(b => b.BarcodeShort).NotEmpty().WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.BarcodeShortCannotBeEmpty);
            RuleFor(b => b.BarcodeLong).Length(systemSettingsService.GetBarcodeLongLength()).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.BarcodeLongHasWrongLength);
            RuleFor(b => b.BarcodeShort).Length(systemSettingsService.GetBarcodeShortBoroughLength(), systemSettingsService.GetBarcodeShortLength()).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.BarcodeShortHasWrongLength);
            RuleFor(b => b.BarcodeLong).Must(ElectionEventIdMustExist).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.ElectionEventIdWasNotFound);
            RuleFor(b => b.BarcodeLong).Must(ElectionIdAndElectionGroupIdMustExist).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.ElectionIdAndElectionGroupIdWasNotFound);
            RuleFor(b => b.BarcodeLong).Must(ContestIdMustExist).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.ContestIdWasNotFound);
            RuleFor(b => b.BarcodeLong).Must(ContentTypeIdMustBeValid).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.ContentTypeIdMustBeValid);
            RuleFor(b => b.BarcodeShort).Must(CountryCodeMustBe47).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.CountryCodeMustBe47);
            RuleFor(b => b.BarcodeShort).Must(CountyCodeMustExist).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.CountyCodeMustExist);
            RuleFor(b => b.BarcodeShort).Must(MunicipalityCodeMustExist).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.MunicipalityCodeMustExist);
            RuleFor(b => b.BarcodeShort).Must(PollingDistrictMustExist).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.PollingDistrictMustExist);

            RuleFor(b => b.BarcodeLong).Must(VotingCategoryMustBeValid).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.VotingCategoryMustBeValid);
            RuleFor(b => b.BarcodeLong).Must(BoxNumberXMustNotBeHigherThanY).WithMessage(Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.BoxNumberXMustNotBeHigherThanY);
        }

        private bool PollingDistrictMustExist(Barcode b, string barcodeShort)
        {
            if (!string.IsNullOrEmpty(barcodeShort)
                && !string.IsNullOrEmpty(b.BarcodeLong)
                && b.BarcodeLong.Length > BarcodeSplitter.LengthOfElectionEventId
                && (barcodeShort.Length == systemSettingsService.GetBarcodeShortLength() || barcodeShort.Length == systemSettingsService.GetBarcodeShortBoroughLength()))
            {

                var electionEventId = b.BarcodeLong.GetElectionEventIdFromBarcodeLong();
                var pollingDistrict = candidateListDataProvider.GetPollingDistrict(electionEventId, barcodeShort);
                return pollingDistrict != null;
            }

            return true;
        }

        private bool MunicipalityCodeMustExist(Barcode b, string barcodeShort)
        {
            if (!string.IsNullOrEmpty(barcodeShort)
                && barcodeShort.Length >= BarcodeSplitter.MunicipalityCodeStartIndex + BarcodeSplitter.LengthOfMunicipalityCode)
            {
                var municipalityCode = barcodeShort.GetMunicipalityCodeFromBarcodeShort();
                var municipality = municipalityDataProvider.GetMunicipality(b.BarcodeLong.GetElectionEventIdFromBarcodeLong(), municipalityCode);
                return municipality != null;
            }

            return true;
        }

        private bool CountyCodeMustExist(Barcode b, string barcodeShort)
        {
            if (!string.IsNullOrEmpty(barcodeShort)
                && barcodeShort.Length >= BarcodeSplitter.CountyCodeStartIndex + BarcodeSplitter.LengthOfCountyCode)
            {
                var countyCode = barcodeShort.GetCountyCodeFromBarcodeShort();

                var county = municipalityDataProvider.GetCounty(b.BarcodeLong.GetElectionEventIdFromBarcodeLong(), countyCode);
                return county != null;
            }

            return true;
        }

        private bool CountryCodeMustBe47(string barcodeShort)
        {
            if (!string.IsNullOrEmpty(barcodeShort) 
                && barcodeShort.Length >= BarcodeSplitter.LengthOfCountryCode)
            {
                return barcodeShort.StartsWith("47");
            }

            return true;
        }

        private bool VotingCategoryMustBeValid(Scan.Presentation.RegisterBarcode.Barcode.Barcode b, string barcodeLong)
        {
            if (!string.IsNullOrEmpty(barcodeLong)
                && (barcodeLong.Length >= BarcodeSplitter.VotingCategoryTypeStartIndex + BarcodeSplitter.LengthOfVotingCategoryType)
                && !string.IsNullOrEmpty(b.BarcodeShort)
                && (b.BarcodeShort.Length == systemSettingsService.GetBarcodeShortBoroughLength() || b.BarcodeShort.Length == systemSettingsService.GetBarcodeShortLength()))
            {
                var electionEventId = barcodeLong.GetElectionEventIdFromBarcodeLong();

                var votingCategoryType = barcodeLong.GetVotingCategoryTypeFromBarcodeLong();

                var votingCategory = electionConfigDataProvider.GetVotingCategory(electionEventId, b.BarcodeShort, votingCategoryType);
                return votingCategory != null;
            }

            return true;
        }

        private bool ContentTypeIdMustBeValid(string barcodeLong)
        {
            if (!string.IsNullOrEmpty(barcodeLong)
                && barcodeLong.Length >= BarcodeSplitter.ContentTypeStartIndex + BarcodeSplitter.LengthOfContentType)
            {
                var contentTypeId = barcodeLong.GetContentTypeFromBarcodeLong();
                return contentTypeId == "1";
            }

            return true;
        }

        private bool BoxNumberXMustNotBeHigherThanY(Barcode b, string barcodeLong)
        {
            if (!string.IsNullOrEmpty(barcodeLong)
                && barcodeLong.Length >= BarcodeSplitter.BoxNumberStartIndex + BarcodeSplitter.LengthOfBoxNumber + BarcodeSplitter.LengthOfTotalBoxCount)
            {
                var boxNumber = barcodeLong.GetBoxNumberFromBarcodeLong();
                var totalBoxes = barcodeLong.GetTotalBoxCountFromBarcodeLong();
                return boxNumber <= totalBoxes;
            }

            return true;
        }

        private bool ContestIdMustExist(string barcodeLong)
        {
            if (!string.IsNullOrEmpty(barcodeLong)
                && barcodeLong.Length >= BarcodeSplitter.ContestIdStartIndex + BarcodeSplitter.LengthOfContestId)
            {
                var electionEventId = barcodeLong.GetElectionEventIdFromBarcodeLong();
                var contestId = barcodeLong.GetContestIdFromBarcodeLong();

                var contest = electionConfigDataProvider.GetContest(electionEventId, contestId);
                return contest != null;
            }

            return true;
        }

        private bool ElectionIdAndElectionGroupIdMustExist(string barcodeLong)
        {
            if (!string.IsNullOrEmpty(barcodeLong)
                && (barcodeLong.Length >= BarcodeSplitter.ElectionGroupIdStartIndex + BarcodeSplitter.LengthOfElectionGroupId)
                && (barcodeLong.Length >= BarcodeSplitter.ElectionIdStartIndex + BarcodeSplitter.LengthOfElectionId))
            {
                var electionEventId = barcodeLong.GetElectionEventIdFromBarcodeLong();
                var electionId = barcodeLong.GetElectionIdFromBarcodeLong();
                var electionGroupId = barcodeLong.GetElectionGroupIdFromBarcodeLong();

                var election = electionConfigDataProvider.GetElection(electionEventId, electionId);
                return (election != null) && (election.GroupId == electionGroupId);
            }

            return true;
        }

        private bool ElectionEventIdMustExist(string barcodeLong)
        {
            if (!string.IsNullOrEmpty(barcodeLong)
                && barcodeLong.Length >= BarcodeSplitter.LengthOfElectionEventId)
            {
                var electionEventId = barcodeLong.GetElectionEventIdFromBarcodeLong();
                var electionEvent = electionConfigDataProvider.GetElectionEvent(electionEventId);
                return electionEvent != null;
            }

            return true;
        }
    }
}
