using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Valg.EVA.Skanning.Contracts;

namespace Valg.EVA.Skanning.Diagnostics
{
    public class TimingProbe : ITimingProbe
    {
        private readonly IDiagnosticsConfiguration diagnosticsConfiguration;

        public TimingProbe(IDiagnosticsConfiguration diagnosticsConfiguration)
        {
            this.diagnosticsConfiguration = diagnosticsConfiguration;
        }

        public void Measure(Action callbackToMeasure, Func<IMeasurementEventData> instrumentationContextGenerator)
        {
            if (diagnosticsConfiguration.IsPerformanceMeasurementProbeEnabled)
            {
                var instrumentedContext = instrumentationContextGenerator();
                using (new TimingScope(timingResult: instrumentedContext))
                {
                    callbackToMeasure();
                }

                DispatchToEventSource(instrumentedContext);
            }
            else
            {
                callbackToMeasure();
            }
        }

        private void DispatchToEventSource(IMeasurementEventData measurementEventData)
        {
            EvaSkanningEventSource.Log.MeasurementRecorded(measurementEventData.EventId, measurementEventData.EventName, measurementEventData.ElapsedTimeInMs, JsonConvert.SerializeObject(measurementEventData));
        }

        public async Task MeasureAsync(Func<Task> callbackToMeasure, Func<IMeasurementEventData> instrumentationContextGenerator)
        {
            if (diagnosticsConfiguration.IsPerformanceMeasurementProbeEnabled)
            {
                var instrumentedContext = instrumentationContextGenerator();
                using (new TimingScope(timingResult: instrumentedContext))
                {
                    await callbackToMeasure().ConfigureAwait(false);
                }

                DispatchToEventSource(instrumentedContext);
            }
            else
            {
                await callbackToMeasure().ConfigureAwait(false);
            }
        }
    }
}
