using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Valg.EVA.Skanning.Configuration;
using Valg.EVA.Skanning.Contracts;
using Valg.EVA.Skanning.Diagnostics;
using Valg.EVA.Skanning.Repositories.System;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.DataProvider;
using Valg.EVA.Skanning.ServicesModel.ElectionConfig.Model;
using Valg.EVA.Skanning.ServicesModel.Internal.SystemSettings;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.SystemServices.Read;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.Version.Read;
using FileVersionInfo = System.Diagnostics.FileVersionInfo;

namespace Valg.EVA.Skanning.Services.SystemServices
{
    public class SystemService : ISystemService
    {
        private readonly ElectionConfigDataPreloader electionConfigDataPreloader;
        private readonly ISystemSettingsProvider systemSettingsProvider;
        private readonly ISystemRepository systemRepository;
        private readonly IElectionConfigMetadataProvider electionConfigMetadataProvider;
        private readonly IDatabaseConfiguration databaseConfiguration;

        public SystemService(ElectionConfigDataPreloader electionConfigDataPreloader,
            ISystemSettingsProvider systemSettingsProvider, 
            ISystemRepository systemRepository, 
            IElectionConfigMetadataProvider electionConfigMetadataProvider, 
            IDatabaseConfiguration databaseConfiguration)
        {
            this.electionConfigDataPreloader = electionConfigDataPreloader;
            this.systemSettingsProvider = systemSettingsProvider;
            this.systemRepository = systemRepository;
            this.electionConfigMetadataProvider = electionConfigMetadataProvider;
            this.databaseConfiguration = databaseConfiguration;
        }

        public void VerifyDatabaseConsistency()
        {
            TestDatabaseConnection();
            TestDatabaseCompatibility();
        }


        private void TestDatabaseConnection()
        {
            try
            {
                systemRepository.ExecuteEmptyCommand();
            }
            catch
            {
                EvaSkanningEventSource.Log.DatabaseConnectionTestFailed(Process.GetCurrentProcess().ProcessName, databaseConfiguration.DatabaseServerName);
                throw new DatabaseConnectionException();
            }
        }

        public void InitElectionConfigDataCache(string electionEventId)
        {
            electionConfigDataPreloader.PreloadData(electionEventId);
        }

        public void InitSettingsCache()
        {
            systemSettingsProvider.PreloadData();
        }

        private void TestDatabaseCompatibility()
        {
            var expectedDatabaseVersion = databaseConfiguration.DatabaseVersion;
            var currentDatabaseVersion = systemRepository.GetDatabaseVersion();
            if (currentDatabaseVersion != expectedDatabaseVersion)
            {
                EvaSkanningEventSource.Log.DatabaseCompatibilityTestFailed(databaseConfiguration.DatabaseVersion, currentDatabaseVersion, databaseConfiguration.DatabaseServerName);
                throw new DatabaseCompatibilityException(currentDatabaseVersion, expectedDatabaseVersion);
            }
        }

        public IsDatabaseAliveResult IsDatabaseAlive()
        {
            var from = systemRepository.IsDatabaseAlive();
            return new IsDatabaseAliveResult { DatabaseExeception = from.DatabaseExeception, IsAlive = from.IsAlive };
        }

        public VersionInfo GetVersionInfo(string electionEventId)
        {
            return new VersionInfo
            {
                DatabaseVersion = systemRepository.GetDatabaseVersion(),
                ProductVersion = GetProductVersion(),
                ConfigurationMetadata = GetConfigurationMetadata(electionEventId)
            };
        }

        private ElectionConfigMetadata GetConfigurationMetadata(string electionEventId)
        {
            return electionConfigMetadataProvider.GetLatestElectionConfigMetadataInfo(electionEventId);
        }
        
        private string GetProductVersion()
        {
            return FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion;
        }
    }
}
