using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;

namespace ScadaLink.SiteEventLogging.Tests;

public class EventLogPurgeServiceTests : IDisposable
{
    private readonly SiteEventLogger _eventLogger;
    private readonly string _dbPath;
    private readonly SiteEventLogOptions _options;

    public EventLogPurgeServiceTests()
    {
        _dbPath = Path.Combine(Path.GetTempPath(), $"test_purge_{Guid.NewGuid()}.db");
        _options = new SiteEventLogOptions
        {
            DatabasePath = _dbPath,
            RetentionDays = 30,
            MaxStorageMb = 1024
        };
        _eventLogger = new SiteEventLogger(
            Options.Create(_options),
            NullLogger<SiteEventLogger>.Instance);
    }

    public void Dispose()
    {
        _eventLogger.Dispose();
        if (File.Exists(_dbPath)) File.Delete(_dbPath);
    }

    private EventLogPurgeService CreatePurgeService(SiteEventLogOptions? optionsOverride = null)
    {
        var opts = optionsOverride ?? _options;
        return new EventLogPurgeService(
            _eventLogger,
            Options.Create(opts),
            NullLogger<EventLogPurgeService>.Instance);
    }

    private void InsertEventWithTimestamp(DateTimeOffset timestamp)
    {
        using var cmd = _eventLogger.Connection.CreateCommand();
        cmd.CommandText = """
            INSERT INTO site_events (timestamp, event_type, severity, source, message)
            VALUES ($ts, 'script', 'Info', 'Test', 'Test message')
            """;
        cmd.Parameters.AddWithValue("$ts", timestamp.ToString("o"));
        cmd.ExecuteNonQuery();
    }

    private long GetEventCount()
    {
        using var cmd = _eventLogger.Connection.CreateCommand();
        cmd.CommandText = "SELECT COUNT(*) FROM site_events";
        return (long)cmd.ExecuteScalar()!;
    }

    [Fact]
    public void PurgeByRetention_DeletesOldEvents()
    {
        // Insert an old event (31 days ago) and a recent one
        InsertEventWithTimestamp(DateTimeOffset.UtcNow.AddDays(-31));
        InsertEventWithTimestamp(DateTimeOffset.UtcNow);

        var purge = CreatePurgeService();
        purge.RunPurge();

        Assert.Equal(1, GetEventCount());
    }

    [Fact]
    public void PurgeByRetention_KeepsRecentEvents()
    {
        InsertEventWithTimestamp(DateTimeOffset.UtcNow.AddDays(-29));
        InsertEventWithTimestamp(DateTimeOffset.UtcNow.AddDays(-1));
        InsertEventWithTimestamp(DateTimeOffset.UtcNow);

        var purge = CreatePurgeService();
        purge.RunPurge();

        Assert.Equal(3, GetEventCount());
    }

    [Fact]
    public void PurgeByStorageCap_DeletesOldestWhenOverCap()
    {
        // Insert enough events to have some data
        for (int i = 0; i < 100; i++)
        {
            InsertEventWithTimestamp(DateTimeOffset.UtcNow);
        }

        // Set an artificially small cap to trigger purge
        var smallCapOptions = new SiteEventLogOptions
        {
            DatabasePath = _dbPath,
            RetentionDays = 30,
            MaxStorageMb = 0  // 0 MB cap forces purge
        };

        var purge = CreatePurgeService(smallCapOptions);
        purge.RunPurge();

        // All events should be purged since cap is 0
        Assert.Equal(0, GetEventCount());
    }

    [Fact]
    public void GetDatabaseSizeBytes_ReturnsPositiveValue()
    {
        InsertEventWithTimestamp(DateTimeOffset.UtcNow);

        var purge = CreatePurgeService();
        var size = purge.GetDatabaseSizeBytes();

        Assert.True(size > 0);
    }
}
