using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Valg.EVA.Skanning.Contracts;
using Valg.EVA.Skanning.Commands;
using Valg.EVA.Skanning.Commands.EmergencyMode;
using Valg.EVA.Skanning.Contracts.Security;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.DataProvider;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.EmergencyMode.Write;
using Valg.EVA.Skanning.Diagnostics;
using Valg.EVA.Skanning.Repositories.EmergencyMode;
using Valg.EVA.Skanning.Services.SystemSettings;

namespace Valg.EVA.Skanning.Services.EmergencyMode
{
    public class EmergencyModeService : IEmergencyModeService
    {
        private readonly IElectionConfigDataProvider electionConfigDataProvider;
        private readonly ICommandDispatcher commandDispatcher;
        private readonly ISystemSettingsService systemSettingsService;
        private readonly IEmergencyModeRepository emergencyModeRepository;
        private static readonly string osloMunicipalityCode = "0301";

        public EmergencyModeService(IElectionConfigDataProvider electionConfigDataProvider, ICommandDispatcher commandDispatcher, ISystemSettingsService systemSettingsService, IEmergencyModeRepository emergencyModeRepository)
        {
            this.electionConfigDataProvider = electionConfigDataProvider;
            this.commandDispatcher = commandDispatcher;
            this.systemSettingsService = systemSettingsService;
            this.emergencyModeRepository = emergencyModeRepository;
        }

        public CreateEmergencyTokenResult CreateEmergencyTokenAndActivate(CreateEmergencyModeTokenParams createTokenParams)
        {
            var result = new CreateEmergencyTokenResult();

            try
            {
                var token = CreateEmergencyToken(createTokenParams);
                SaveToken(token, createTokenParams.CreatedBy);
                result.WasSuccessful = true;
            }
            catch (Exception e)
            {
                EvaSkanningEventSource.Log.ActivatingEmergencyModeFailed(e.ToString());
                result.WasSuccessful = false;
            }
            return result;
        }

        public string CreateEmergencyToken(CreateEmergencyModeTokenParams createTokenParams)
        {
            var token = ReplaceElectionEventData(createTokenParams.ElectionEvent);

            if (createTokenParams.CountingAs == CountingAs.County)
            {
                token = AddCountyRole(token, createTokenParams);
            }
            else if (createTokenParams.CountingAs == CountingAs.Municipality)
            {
                token = AddMunicipalityRoles(token, createTokenParams);
            }

            return token;
        }

        public void StopEmergencyMode(string modifiedBy)
        {
            StopEmergencyModeCommand command = new StopEmergencyModeCommand(modifiedBy);
            commandDispatcher.DispatchCommand(command);
        }

        public bool IsEmergencyModeActive()
        {
            return emergencyModeRepository.IsEmergencyModeActive();
        }

        public string GetActiveToken()
        {
            return emergencyModeRepository.GetActiveToken();
        }

        private void SaveToken(string token, string createdBy)
        {
            var command = new CreateEmergencyModeCommand(token, createdBy);
            commandDispatcher.DispatchCommand(command);
        }

        private string AddMunicipalityRoles(string token, CreateEmergencyModeTokenParams createTokenParams)
        {
            StringBuilder roleTokens = new StringBuilder();
            foreach (var municipality in createTokenParams.SelectedMunicipalities)
            {
                var role = TokenTemplates.RoleTemplate.Replace("[electionContextId]", createTokenParams.ElectionEvent.Id);
                role = role.Replace("[electionContextName]", createTokenParams.ElectionEvent.Name);

                role = role.Replace("[areaContextId]", $"{createTokenParams.ElectionEvent.Id}.47.{municipality.CountyCode}.{municipality.Code}");
                role = role.Replace("[areaContextName]", municipality.Name);

                var reportingUnit = electionConfigDataProvider.GetReportingUnit(createTokenParams.ElectionEvent.Id, $"47.{municipality.CountyCode}.{municipality.Code}");
                role = role.Replace("[reportingUnitId]", reportingUnit.Id);
                role = role.Replace("[reportingUnitName]", reportingUnit.Name);

                AddBydelsvalgOslo(roleTokens, createTokenParams, municipality);

                roleTokens.Append(role);
            }

            return token.Replace("[roles]", roleTokens.ToString());
        }

        private void AddBydelsvalgOslo(StringBuilder roleTokens, CreateEmergencyModeTokenParams createTokenParams, ServicesModel.ElectionConfig.Model.Municipality municipality)
        {
            if (IsOslo(municipality))
            {
                var contests = electionConfigDataProvider.GetContestsInElection(createTokenParams.ElectionEvent.Id);
                
                if (contests.Any(c => c.Id.StartsWith(osloMunicipalityCode)))
                {
                    var bydeler = GetBydelerInOslo();
                    foreach (var bydel in bydeler)
                    {
                        var role = TokenTemplates.RoleBydelsvalgOsloTemplate.Replace("[electionContextId]", createTokenParams.ElectionEvent.Id);
                        role = role.Replace("[electionContextName]", createTokenParams.ElectionEvent.Name);

                        role = role.Replace("[areaContextId]", $"{createTokenParams.ElectionEvent.Id}.47.{municipality.CountyCode}.{municipality.Code}.{bydel.ContestId}");
                        role = role.Replace("[areaContextName]", bydel.ContestName);

                        roleTokens.Append(role);
                    }
                }
            }
        }

        private IEnumerable<Bydel> GetBydelerInOslo()
        {
            var json = systemSettingsService.GetOsloDistrictList();
            return JsonConvert.DeserializeObject<List<Bydel>>(json);
        }

        private bool IsOslo(ServicesModel.ElectionConfig.Model.Municipality municipality)
        {
            return municipality.Code == osloMunicipalityCode;
        }

        private string AddCountyRole(string token, CreateEmergencyModeTokenParams createTokenParams)
        {
            var role = TokenTemplates.RoleTemplate.Replace("[electionContextId]", createTokenParams.ElectionEvent.Id);
            role = role.Replace("[electionContextName]", createTokenParams.ElectionEvent.Name);

            role = role.Replace("[areaContextId]", $"{createTokenParams.ElectionEvent.Id}.47.{createTokenParams.SelectedCounty.Code}");
            role = role.Replace("[areaContextName]", createTokenParams.SelectedCounty.Name);

            var reportingUnit = electionConfigDataProvider.GetReportingUnit(createTokenParams.ElectionEvent.Id, $"47.{createTokenParams.SelectedCounty.Code}");
            role = role.Replace("[reportingUnitId]", reportingUnit.Id);
            role = role.Replace("[reportingUnitName]", reportingUnit.Name);

            return token.Replace("[roles]", role);
        }

        private string ReplaceElectionEventData(ServicesModel.ElectionConfig.Model.ElectionEvent electionEvent)
        {
            var token = TokenTemplates.TokenTemplate.Replace("[electionEventId]", electionEvent.Id);
            token = token.Replace("[electionEventName]", electionEvent.Name);
            return token;
        }

        private class Bydel
        {
            public string ContestName { get; set; }

            public string ContestId { get; set; }
        }
    }
}
