using System;
using System.Linq;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.CountResult.Write;
using Valg.EVA.Skanning.Signing;
using Valg.EVA.Skanning.Tools;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Valg.EVA.Skanning.Contracts;
using Valg.EVA.Skanning.Diagnostics;

namespace Valg.EVA.Skanning.Services.CountResult
{
    public class CountingResultService : ICountingResultService
    {
        private readonly CountingResultGenerator resultGenerator;
        private readonly CountingEmlGenerator emlGenerator;
        private readonly ISignatureCryptographicServiceProvider cryptographicService;
        private readonly ITimingProbe timingProbe;
        private readonly IDatabaseConfiguration databaseConfiguration;

        public CountingResultService(
            CountingResultGenerator resultGenerator,
            CountingEmlGenerator emlGenerator,
            ISignatureCryptographicServiceProvider cryptographicService,
            ITimingProbe timingProbe, 
            IDatabaseConfiguration databaseConfiguration)
        {
            this.resultGenerator = resultGenerator;
            this.emlGenerator = emlGenerator;
            this.cryptographicService = cryptographicService;
            this.timingProbe = timingProbe;
            this.databaseConfiguration = databaseConfiguration;
        }

        public GenerateCountingResultResult GenerateCountingResult(GenerateCountingResultParameters parameters)
        {
            return resultGenerator.CreateResultForCounting(parameters);
        }

        public GenerateCountingEmlResult GetCountingAsEml(GenerateCountingEmlParameters parameters)
        {
            GenerateCountingEmlResult result = null;
            timingProbe.Measure(() => result = emlGenerator.CreateEmlForResult(parameters), () => new DefaultMeasurementEventData(parameters.ReportingUnitIdentifier,
                (int)ServicesInstrumentedEvents.CreateCountingResultXmlFiles, ServicesInstrumentedEvents.CreateCountingResultXmlFiles.ToString(), databaseConfiguration.DatabaseServerName));

            var votes = ZipVotesAndimages(result);
            result.SigningResult = cryptographicService.Sign(votes);

            if (result.SigningResult.IsSigningSuccessful)
            {
                timingProbe.Measure(() =>
                {
                    var filesToZip = new List<ZipBuilder.FileDefinition>
                    {
                        new ZipBuilder.FileDefinition("Counts.zip", votes),
                        new ZipBuilder.FileDefinition("Counts.zip.signature", CreateSignatureXml(result.SigningResult.SigningCertificate,result.SigningResult.SignedData))
                        
                    };

                    result.ZipFile = ZipBuilder.CreateZipFile(filesToZip);
                }, () => new DefaultMeasurementEventData(parameters.ReportingUnitIdentifier,
                    (int) ServicesInstrumentedEvents.PackageCountingResultXmlFilesInZip,
                    ServicesInstrumentedEvents.PackageCountingResultXmlFilesInZip.ToString(),
                    databaseConfiguration.DatabaseServerName));
            }
            return result;
        }

        private string CreateSignatureXml(X509Certificate2 signatureCertificate, byte[] signature)
        {
            var xml = $"<SignatureFile>";
            if (signatureCertificate != null)
            {
                xml += $"<PublicCertificate>{Convert.ToBase64String(signatureCertificate.RawData)}</PublicCertificate>";
            }
            else
            {
                xml += $"<PublicCertificate></PublicCertificate>";
            }
                      xml += $"<Signature>{Convert.ToBase64String(signature)}</Signature>" +
                   $"</SignatureFile>";
            return xml;
        }

        private static byte[] ZipVotesAndimages(GenerateCountingEmlResult result)
        {
            var filesToZip = new List<ZipBuilder.FileDefinition>();
            if (!string.IsNullOrWhiteSpace(result.CountXml))
                filesToZip.Add(new ZipBuilder.FileDefinition("Count-0001.xml", Encoding.UTF8.GetBytes(result.CountXml)));
            if (!string.IsNullOrWhiteSpace(result.VoteXml))
                filesToZip.Add(new ZipBuilder.FileDefinition("Votes-0001.xml", Encoding.UTF8.GetBytes(result.VoteXml)));

            filesToZip.AddRange(result.Images.Select(image => new ZipBuilder.FileDefinition(image.Key, image.Value)));

            return ZipBuilder.CreateZipFile(filesToZip);
        }
    }
}
