using Inspectron.Statistics;    
using Lindt.Colorballs.Duo.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection.PortableExecutable;
using Ninject;
using Serilog;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Processing;

namespace VisionBuilder.UI.Statistics
{
    public class VisionBuilderStatistics:IVisionBuilderModule,IDisposable
    {
        private CsvStatistics? _csvStatistics;
        private readonly VisionBuilderStatisticsSettings _settings;
        private string _filePath;
        private readonly IRecognitionControl _recognitionControl;
        private readonly ILoadingService _loadingService;
        private DateTime _startTime;
        private string _currentRecipe;
        private string _fileName;
        private string _directoryPath;
        private int _total = 0;
        private int _errors = 0;

        private const string BY_EVENTS_FILE_NAME = "statistics_by_events.csv";
        private const string MONTHLY_FILE_NAME = "statistics_month.csv";
        private const string EMPTY_DATE = "";
        private const string EMPTY_TIME = "";
        private const string DATE_FORMAT = "dd.MM.yyyy";
        private const string TIME_FORMAT = "HH:mm:ss";
        private const string TIME_FORMAT_MS = "HH:mm:ss:fff";
        public bool DoNotDeleteTempFiles { get; set; } = false;

        public VisionBuilderStatistics(VisionBuilderStatisticsSettings settings, IRecognitionControl recognitionControl, ILoadingService loadingService)
        {
            _settings = settings;
            _recognitionControl = recognitionControl;
            _loadingService = loadingService;


        }

        public string Metadata { get; set; } = "";

        private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
        {
            if (_csvStatistics == null)
            {
                Log.Warning("Session ended but CSV statistics is null, nothing to do.");
                return;
            }
            var endedAt = obj.SessionEnded;
            _endTime = endedAt;
            Log.Information("Start processing statistics");
            _loadingService.StartLoading("Writing statistics for " + _settings.CameraName);
            try
            {
                _csvStatistics.WriteLine([_startTime.ToString(DATE_FORMAT), _startTime.ToString(TIME_FORMAT), endedAt.ToString(DATE_FORMAT), endedAt.ToString(TIME_FORMAT), "", "", ""]);
                _csvStatistics.Dispose();
                _csvStatistics = null;

                WriteFinalStatistics();
            }
            finally
            {
                _loadingService.StopLoading("Writing statistics for " + _settings.CameraName);
                Log.Information("End processing statistics");
            }
        }

        private void _recognitionControl_ImageProcessed(ImageProcessedEvent obj)
        {
            var sw = Stopwatch.StartNew();
            _total++;
            if (obj.HasError)
            {
                _errors++;
                RecordBadImage(obj.ErrorNames.ToArray());
            }
            sw.Stop();
            Log.Debug($"Recording statistics took {sw.ElapsedMilliseconds} ms");
        }

        private void _recognitionControl_SessionStarted(SessionStartedEvent obj)
        {
            _startTime = obj.SessionStarted;
            _total = 0;
            _errors= 0;
            _currentRecipe = obj.RecipeName;
            _fileName = BY_EVENTS_FILE_NAME;
            _directoryPath = PathSettingsUtils.ConvertVariables(_settings.PathTemplate, _startTime, _startTime,
                obj.RecipeName, cameraName: _settings.CameraName);
            _filePath = Path.Combine(_directoryPath, _fileName);
            var headers = new[] { _settings.StartDateColumnName, _settings.StartTimeColumnName, _settings.EndDateColumnName, _settings.EndTimeColumnName, _settings.ErrorTimeColumnName, _settings.ProductColumnName, _settings.ErrorColumnName, _settings.MetadataColumnName };
            if (!Directory.Exists(Path.GetDirectoryName(_filePath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(_filePath));
            }
            _csvStatistics = new CsvStatistics(_filePath, headers);
            _csvStatistics.WriteLine([_startTime.ToString(DATE_FORMAT), _startTime.ToString(TIME_FORMAT)]); // session started
            _defects = new ConcurrentDictionary<string, int>();
        }

        ConcurrentDictionary<string, int> _defects = new();
        private DateTime _endTime;

        private void RecordBadImage(string[] defectNames)
        {
  
            _csvStatistics.WriteLine(

            [_startTime.ToString(DATE_FORMAT), _startTime.ToString(TIME_FORMAT), EMPTY_DATE, EMPTY_TIME, DateTime.Now.ToString(DATE_FORMAT+" "+TIME_FORMAT_MS), _currentRecipe, defectNames[0], Metadata]

                );
            foreach (var defectName in defectNames)
            {
                _defects.AddOrUpdate(defectName, 1, (key, oldValue) => oldValue + 1);
            }
        }

        private void WriteMonthStatistics()
        {

            var startDate = _startTime.Date;
            var startTime = _startTime;
            var endDate = _endTime.Date;
            var endTime = _endTime;

            var monthFilePath = Path.Combine(_directoryPath, MONTHLY_FILE_NAME);

            var monthStatistics = new CsvStatistics(monthFilePath, [
               
            ]);

            monthStatistics.WriteLine(
                ["Start", _startTime.ToString($"{DATE_FORMAT} {TIME_FORMAT}")]
            );
            monthStatistics.WriteLine([
                _settings.MetadataColumnName, Metadata
                ]);
            monthStatistics.WriteLine(
                [_settings.ProductColumnName, _currentRecipe]
            );
            monthStatistics.WriteLine(
                [_settings.ErrorColumnName, "Amount"]
            );

            foreach (var defect in _defects.OrderBy(x=>x.Key))
            {
                monthStatistics.WriteLine(
                    [defect.Key, defect.Value.ToString()]
                );
            }



            monthStatistics.WriteLine([]);
            monthStatistics.WriteLine(
                ["Total error images", _errors.ToString()]
            );
            monthStatistics.WriteLine(
                ["Total produced(including errors)", _total.ToString()]
            );

            var percentage = _total > 0 ? (double)_errors / _total * 100 : 0;

            monthStatistics.WriteLine(
                ["Error rate", percentage.ToString("F2") + "%"]
            );

            monthStatistics.WriteLine(
            [
                "End", _endTime.ToString($"{DATE_FORMAT} {TIME_FORMAT}")
                ]);

            monthStatistics.WriteLine([]);
            monthStatistics.WriteLine([]);
            monthStatistics.Dispose();


        }

        private void WriteFinalStatistics()
        {
            try
            {
                Log.Debug("Writing month statistics");
                WriteMonthStatistics();
            }
            catch (Exception e)
            {
                Log.Error("Error writing statistics for " + _settings.CameraName+ "\n"+ e);
            }
            _csvStatistics?.Dispose();
            _csvStatistics = null;
        }

        public void InitializeModule()
        {
            _recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
            _recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
            _recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
        }

        public void Dispose()
        {
            _csvStatistics?.Dispose();
            _recognitionControl.SessionStarted -= _recognitionControl_SessionStarted;
            _recognitionControl.ImageProcessed -= _recognitionControl_ImageProcessed;
            _recognitionControl.SessionEnded -= _recognitionControl_SessionEnded;
        }
    }
}
