using System.Collections.Concurrent;
using System.Reflection;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Processing;
using VisionBuilder.UI.Common.ViewModel.Classes;
using VisionBuilder.UI.Statistics;

namespace VisionBuilder.UI.Tests;

[TestClass]
public class VisionBuilderStatisticsTests
{
    private string _tempDirectory;
    private VisionBuilderStatisticsSettings _settings;
    private TestRecognitionControl _recognitionControl;
    private TestLoadingService _loadingService;

    [TestInitialize]
    public void Setup()
    {
        _tempDirectory = Path.Combine(Path.GetTempPath(), "VisionBuilderStatisticsTests", Guid.NewGuid().ToString());
        Directory.CreateDirectory(_tempDirectory);
        
        _settings = new VisionBuilderStatisticsSettings("TestCamera")
        {
            PathTemplate = _tempDirectory,
            StartDateColumnName = "Start Date",
            StartTimeColumnName = "Start time",
            EndDateColumnName = "End Date",
            EndTimeColumnName = "End time",
            ErrorTimeColumnName = "Error time",
            ProductColumnName = "Product",
            ErrorColumnName = "Error"
        };
        
        _recognitionControl = new TestRecognitionControl();
        _loadingService = new TestLoadingService();
    }

    [TestCleanup]
    public void Cleanup()
    {
        if (Directory.Exists(_tempDirectory))
        {
            Directory.Delete(_tempDirectory, true);
        }
    }

    [TestMethod]
    public void SessionStarted_CreatesTemporaryFile_WithCorrectHeaders()
    {
        // Arrange
        var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
        statistics.InitializeModule();
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "TestRecipe",
            SessionStarted = DateTime.Now
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);
        statistics.Dispose(); // Ensure file is written and closed

        // Assert
        var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
        Assert.AreEqual(1, tmpFiles.Length, "Should create exactly one temporary file");
        
        var lines = File.ReadAllLines(tmpFiles[0]);
        Assert.IsTrue(lines.Length >= 2, "Should have header and at least one data line");
        
        var expectedHeaders = new[] { "Start Date", "Start time", "End Date", "End time", "Error time", "Product", "Error" };
        var actualHeaders = lines[0].Split(',');
        CollectionAssert.AreEqual(expectedHeaders, actualHeaders, "Headers should match settings");
        
        var firstDataLine = lines[1].Split(',');
        Assert.IsTrue(DateTime.TryParse(firstDataLine[0], out _), "First column should be a valid date");
        Assert.IsTrue(DateTime.TryParse(firstDataLine[1], out _), "Second column should be a valid time");
    }

    [TestMethod]
    public void ImageProcessed_WithError_RecordsDefectInFile()
    {
        var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
        statistics.InitializeModule();
        // Arrange
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "TestRecipe",
            SessionStarted = DateTime.Now
        };
        
        var imageProcessedEvent = new ImageProcessedEvent
        {
            HasError = true,
            ErrorNames = new List<string> { "TestDefect" },
            RecipeName = "TestRecipe",
            SessionStart = DateTime.Now
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);
        _recognitionControl.TriggerImageProcessed(imageProcessedEvent);
        statistics.Dispose(); // Ensure file is written and closed
        // Assert
        var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
        var lines = File.ReadAllLines(tmpFiles[0]);
        
        Assert.IsTrue(lines.Length >= 3, "Should have header, session start, and error record");
        
        var errorLine = lines[2].Split(',');
        Assert.AreEqual("TestDefect", errorLine[6], "Error name should be recorded in correct column");
        Assert.AreEqual("TestRecipe", errorLine[5], "Recipe name should be recorded in correct column");
        Assert.IsTrue(DateTime.TryParse(errorLine[4], out _), "Error time should be valid");
        
        // Verify defect counting using reflection
        var defectsField = typeof(VisionBuilderStatistics).GetField("_defects", BindingFlags.NonPublic | BindingFlags.Instance);
        var defects = (ConcurrentDictionary<string, int>)defectsField.GetValue(statistics);
        Assert.AreEqual(1, defects["TestDefect"], "Defect count should be incremented");
    }

    [TestMethod]
    public void ImageProcessed_WithoutError_DoesNotRecordDefect()
    {
        var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
        statistics.InitializeModule();
        // Arrange
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "TestRecipe",
            SessionStarted = DateTime.Now
        };
        
        var imageProcessedEvent = new ImageProcessedEvent
        {
            HasError = false,
            ErrorNames = new List<string>(),
            RecipeName = "TestRecipe",
            SessionStart = DateTime.Now
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);
        _recognitionControl.TriggerImageProcessed(imageProcessedEvent);
        statistics.Dispose(); // Ensure file is written and closed
        // Assert
        var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
        var lines = File.ReadAllLines(tmpFiles[0]);
        
        Assert.AreEqual(2, lines.Length, "Should only have header and session start line");
        
        // Verify defect counting using reflection
        var defectsField = typeof(VisionBuilderStatistics).GetField("_defects", BindingFlags.NonPublic | BindingFlags.Instance);
        var defects = (ConcurrentDictionary<string, int>)defectsField.GetValue(statistics);
        Assert.AreEqual(0, defects.Count, "No defects should be recorded");
    }

    [TestMethod]
    public void SessionEnded_WritesEndTimeAndDisposesCsv()
    {
        var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService)
        {
            DoNotDeleteTempFiles = true
        };
        statistics.InitializeModule();
        // Arrange
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "TestRecipe",
            SessionStarted = DateTime.Now
        };
        
        var sessionEndEvent = new SessionEndedEvent
        {
            SessionEnded = DateTime.Now
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);
        _recognitionControl.TriggerSessionEnded(sessionEndEvent);
        statistics.Dispose(); // Ensure file is written and closed

        // Assert
        var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
        var lines = File.ReadAllLines(tmpFiles[0]);
        
        Assert.IsTrue(lines.Length >= 3, "Should have header, session start, and session end");
        
        var endLine = lines[2].Split(',');
        Assert.IsTrue(DateTime.TryParse(endLine[2], out _), "End date should be valid");
        Assert.IsTrue(DateTime.TryParse(endLine[3], out _), "End time should be valid");
        
        Assert.IsTrue(_loadingService.StartLoadingCalled, "Should start loading during session end");
        Assert.IsTrue(_loadingService.StopLoadingCalled, "Should stop loading after session end");
    }

    [TestMethod]
    public void WriteFinalStatistics_ConsolidatesTemporaryFiles()
    {
        var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
        statistics.InitializeModule();
        // Arrange
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "TestRecipe",
            SessionStarted = DateTime.Now
        };
        
        var imageProcessedEvent = new ImageProcessedEvent
        {
            HasError = true,
            ErrorNames = new List<string> { "TestDefect" },
            RecipeName = "TestRecipe",
            SessionStart = DateTime.Now
        };
        
        var sessionEndEvent = new SessionEndedEvent
        {
            SessionEnded = DateTime.Now
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);
        _recognitionControl.TriggerImageProcessed(imageProcessedEvent);
        _recognitionControl.TriggerSessionEnded(sessionEndEvent);
        
        statistics.Dispose(); // Ensure file is written and closed

        // Assert
        var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
        Assert.AreEqual(0, tmpFiles.Length, "Temporary files should be deleted after consolidation");
        
        var finalFile = Path.Combine(_tempDirectory, "statistics_by_events.csv");
        Assert.IsTrue(File.Exists(finalFile), "Final statistics file should be created");
        
        var finalLines = File.ReadAllLines(finalFile);
        Assert.IsTrue(finalLines.Length >= 2, "Final file should contain session data");
        
        // Verify the defect was recorded in the final file
        var defectLine = finalLines.FirstOrDefault(l => l.Contains("TestDefect"));
        Assert.IsNotNull(defectLine, "Defect should be recorded in final file");
    }

    [TestMethod]
    public void MultipleDefects_CountedCorrectly()
    {
        var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
        statistics.InitializeModule();
        // Arrange
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "TestRecipe",
            SessionStarted = DateTime.Now
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);
        
        // Process multiple images with same defect
        for (int i = 0; i < 3; i++)
        {
            _recognitionControl.TriggerImageProcessed(new ImageProcessedEvent
            {
                HasError = true,
                ErrorNames = new List<string> { "DefectA" },
                RecipeName = "TestRecipe",
                SessionStart = DateTime.Now
            });
        }
        
        // Process images with different defect
        for (int i = 0; i < 2; i++)
        {
            _recognitionControl.TriggerImageProcessed(new ImageProcessedEvent
            {
                HasError = true,
                ErrorNames = new List<string> { "DefectB" },
                RecipeName = "TestRecipe",
                SessionStart = DateTime.Now
            });
        }
        statistics.Dispose(); // Ensure file is written and closed
        // Assert
        var defectsField = typeof(VisionBuilderStatistics).GetField("_defects", BindingFlags.NonPublic | BindingFlags.Instance);
        var defects = (ConcurrentDictionary<string, int>)defectsField.GetValue(statistics);
        
        Assert.AreEqual(3, defects["DefectA"], "DefectA should be counted correctly");
        Assert.AreEqual(2, defects["DefectB"], "DefectB should be counted correctly");
    }

    [TestMethod]
    public void PathTemplate_WithVariables_ReplacedCorrectly()
    {
        // Arrange
        var customSettings = new VisionBuilderStatisticsSettings("TestCamera")
        {
            PathTemplate = Path.Combine(_tempDirectory, "$RECIPE_NAME", "$SS_YEAR", "$SS_MONTH")
        };
        
        var customStatistics = new VisionBuilderStatistics(customSettings, _recognitionControl, _loadingService);
        customStatistics.InitializeModule();
        var testDate = new DateTime(2023, 5, 15, 10, 30, 0);
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "MyRecipe",
            SessionStarted = new DateTime(2023, 5, 15, 10, 30, 0)
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);
        customStatistics.Dispose(); // Ensure file is written and closed
        // Assert
        var expectedPath = Path.Combine(_tempDirectory, "MyRecipe", testDate.ToString("yyyy"), testDate.ToString("MMMM"));
        Assert.IsTrue(Directory.Exists(expectedPath), "Directory should be created with expanded variables");
        
        var tmpFiles = Directory.GetFiles(expectedPath, "tmp_*.csv");
        Assert.AreEqual(1, tmpFiles.Length, "Temporary file should be created in correct directory");
    }

    [TestMethod]
    public void CustomColumnNames_UsedInHeaders()
    {
        // Arrange
        var customSettings = new VisionBuilderStatisticsSettings("TestCamera")
        {
            PathTemplate = _tempDirectory,
            StartDateColumnName = "Custom Start Date",
            StartTimeColumnName = "Custom Start Time",
            EndDateColumnName = "Custom End Date",
            EndTimeColumnName = "Custom End Time",
            ErrorTimeColumnName = "Custom Error Time",
            ProductColumnName = "Custom Product",
            ErrorColumnName = "Custom Error"
        };
        
        var customStatistics = new VisionBuilderStatistics(customSettings, _recognitionControl, _loadingService);
        customStatistics.InitializeModule();
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "TestRecipe",
            SessionStarted = DateTime.Now
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);

        customStatistics.Dispose();

        // Assert
        var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
        var lines = File.ReadAllLines(tmpFiles[0]);
        
        var expectedHeaders = new[] { "Custom Start Date", "Custom Start Time", "Custom End Date", "Custom End Time", "Custom Error Time", "Custom Product", "Custom Error" };
        var actualHeaders = lines[0].Split(',');
        CollectionAssert.AreEqual(expectedHeaders, actualHeaders, "Custom column names should be used in headers");
    }

    [TestMethod]
    public void WriteMonthStatistics_CreatesMonthlyFileWithDefectCounts()
    {
        var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
        statistics.InitializeModule();
        // Arrange
        var sessionStartEvent = new SessionStartedEvent
        {
            RecipeName = "TestRecipe",
            SessionStarted = new DateTime(2023, 5, 15, 10, 30, 0)
        };
        
        var imageProcessedEvent1 = new ImageProcessedEvent
        {
            HasError = true,
            ErrorNames = new List<string> { "DefectA" },
            RecipeName = "TestRecipe",
            SessionStart = new DateTime(2023, 5, 15, 10, 30, 0)
        };
        
        var imageProcessedEvent2 = new ImageProcessedEvent
        {
            HasError = true,
            ErrorNames = new List<string> { "DefectB" },
            RecipeName = "TestRecipe",
            SessionStart = new DateTime(2023, 5, 15, 10, 30, 0)
        };
        
        var sessionEndEvent = new SessionEndedEvent
        {
            SessionEnded = new DateTime(2023, 5, 15, 11, 45, 0)
        };

        // Act
        _recognitionControl.TriggerSessionStarted(sessionStartEvent);
        _recognitionControl.TriggerImageProcessed(imageProcessedEvent1);
        _recognitionControl.TriggerImageProcessed(imageProcessedEvent1); // Same defect again
        _recognitionControl.TriggerImageProcessed(imageProcessedEvent2);
        _recognitionControl.TriggerSessionEnded(sessionEndEvent);
        statistics.Dispose();

        // Assert
        var monthlyFile = Path.Combine(_tempDirectory, "statistics_month.csv");
        Assert.IsTrue(File.Exists(monthlyFile), "Monthly statistics file should be created");
        
        var monthlyLines = File.ReadAllLines(monthlyFile);
        Assert.IsTrue(monthlyLines.Length >= 3, "Should have header and defect records");
        
        var expectedHeaders = new[] { "Date start", "Time start", "Date end", "Time end", "Product", "Error type", "Error count" };
        var actualHeaders = monthlyLines[0].Split(',');
        CollectionAssert.AreEqual(expectedHeaders, actualHeaders, "Monthly file should have correct headers");
        
        // Verify defect counts
        var defectALine = monthlyLines.FirstOrDefault(l => l.Contains("DefectA"));
        var defectBLine = monthlyLines.FirstOrDefault(l => l.Contains("DefectB"));
        
        Assert.IsNotNull(defectALine, "DefectA should be recorded in monthly file");
        Assert.IsNotNull(defectBLine, "DefectB should be recorded in monthly file");
        
        var defectAData = defectALine.Split(',');
        var defectBData = defectBLine.Split(',');
        
        Assert.AreEqual("2", defectAData[6], "DefectA count should be 2");
        Assert.AreEqual("1", defectBData[6], "DefectB count should be 1");
        Assert.AreEqual("TestRecipe", defectAData[4], "Product name should be correct");
        Assert.AreEqual("TestRecipe", defectBData[4], "Product name should be correct");
        
        // Verify dates and times
        Assert.IsTrue(DateTime.TryParse(defectAData[0], out var startDate), "Start date should be valid");
        Assert.IsTrue(DateTime.TryParse(defectAData[1], out var startTime), "Start time should be valid");
        Assert.IsTrue(DateTime.TryParse(defectAData[2], out var endDate), "End date should be valid");
        Assert.IsTrue(DateTime.TryParse(defectAData[3], out var endTime), "End time should be valid");
    }

    
}

// Test implementation of IRecognitionControl using reflection approach
public class TestRecognitionControl : IRecognitionControl
{
    public void Resume()
    {
        
    }

    public event Action<ImageProcessedEvent> ImageProcessed = delegate { };
    public event Action<SessionStartedEvent> SessionStarted = delegate { };
    public event Action<SessionEndedEvent> SessionEnded = delegate { };

    public List<RecipeData> GetRecipesData() => new List<RecipeData>();
    public void SetRecipe(RecipeData recipe) { }
    public void Start() { }
    public void Stop() { }
    public void Pause()
    {
        
    }

    public void TriggerImageProcessed(ImageProcessedEvent eventArgs) => ImageProcessed(eventArgs);
    public void TriggerSessionStarted(SessionStartedEvent eventArgs) => SessionStarted(eventArgs);
    public void TriggerSessionEnded(SessionEndedEvent eventArgs) => SessionEnded(eventArgs);
}

// Test implementation of ILoadingService
public class TestLoadingService : ILoadingService
{
    public bool StartLoadingCalled { get; private set; }
    public bool StopLoadingCalled { get; private set; }
    public string LastLoadingMessage { get; private set; }

    public void StartLoading(string message)
    {
        StartLoadingCalled = true;
        LastLoadingMessage = message;
    }

    public void StopLoading(string message)
    {
        StopLoadingCalled = true;
        LastLoadingMessage = message;
    }
}