using System.Data;
using Autofac;
using Insight.Database;
using Newtonsoft.Json;
using PlanTempus.Core.Database.ConnectionFactory;
using Shouldly;

namespace PlanTempus.X.TDD.ConfigurationSystem;

[TestClass]
public class SetupConfigurationTests : TestFixture
{
    private IDbConnection _connection;

    [TestInitialize]
    public void Setup()
    {
        var connectionFactory = Container.Resolve<IDbConnectionFactory>();
        _connection = connectionFactory.Create();
    }

    [TestCleanup]
    public void Cleanup()
    {
        _connection.ExecuteSql(@"
            TRUNCATE TABLE app_configuration_history;
            TRUNCATE TABLE app_configuration CASCADE;");

        _connection.Dispose();
    }

    [TestMethod]
    public void InsertConfiguration_ShouldCreateHistoryRecord()
    {
        // Arrange
        var configData = new
        {
            key = "test.key",
            value = "test value",
            label = "Test Label"
        };

        // Act
        var result = _connection.QuerySql<dynamic>(@"
            INSERT INTO app_configuration (key, value, label)
            VALUES (@key, @value, @label)
            RETURNING *", configData).Single();

        var history = _connection.QuerySql<dynamic>(@"
            SELECT key, value, label, action_type 
            FROM app_configuration_history 
            WHERE id = @id AND action_type = 'I'",
                new { id = (int)result.id })
            .Single();

        // Assert
        var expected = JsonConvert.SerializeObject(new
        {
            configData.key,
            configData.value,
            configData.label,
            action_type = "I"
        });
        var actual = JsonConvert.SerializeObject(history) as string;
        actual.ShouldBe(expected);
    }

    [TestMethod]
    public void UpdateConfiguration_ShouldUpdateModifiedAt()
    {
        // Arrange
        var configData = new
        {
            key = "test.key",
            value = "original value"
        };

        var original = _connection.QuerySql<dynamic>(@"
            INSERT INTO app_configuration (key, value)
            VALUES (@key, @value)
            RETURNING modified_at", configData)
            .Single();

        Thread.Sleep(1000);

        // Act
        var updated = _connection.QuerySql<dynamic>(@"
            UPDATE app_configuration 
            SET value = @value
            WHERE key = @key
            RETURNING modified_at",
                new { configData.key, value = "updated value" })
            .Single();

        // Assert
        ((DateTime)updated.modified_at).ShouldBeGreaterThan((DateTime)original.modified_at);
    }

    [TestMethod]
    public void DeleteConfiguration_ShouldCreateHistoryRecord()
    {
        // Arrange
        var configData = new
        {
            key = "test.key",
            value = "test value"
        };

        var original = _connection.QuerySql<dynamic>(@"
            INSERT INTO app_configuration (key, value)
            VALUES (@key, @value)
            RETURNING id", configData)
            .Single();

        // Act
        _connection.ExecuteSql(
            "DELETE FROM app_configuration WHERE id = @id",
            new { id = (int)original.id });

        // Assert
        var history = _connection.QuerySql<dynamic>(@"
            SELECT key, value, action_type 
            FROM app_configuration_history 
            WHERE id = @id AND action_type = 'D'",
                new { id = (int)original.id })
            .Single();

        var expected = JsonConvert.SerializeObject(new
        {
            configData.key,
            configData.value,
            action_type = "D"
        });
        var actual = JsonConvert.SerializeObject(history) as string;
        actual.ShouldBe(expected);
    }

    [TestMethod]
    public void InsertConfiguration_ShouldSetAllColumns()
    {
        // Arrange
        var now = DateTime.UtcNow;
        now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc);
        var configData = new
        {
            key = "test.columns",
            value = "test value",
            label = "Test Label",
            content_type = "application/json",
            valid_from = now,
            expires_at = now.AddDays(30)
        };

        // Act
        var result = _connection.QuerySql<dynamic>(@"
            INSERT INTO app_configuration (
                key, 
                value, 
                label, 
                content_type,
                valid_from,
                expires_at)
            VALUES (
                @key, 
                @value, 
                @label, 
                @content_type,
                @valid_from,
                @expires_at)
            RETURNING key, value, label, content_type, 
                     CAST(EXTRACT(EPOCH FROM date_trunc('minute', valid_from)) AS INTEGER) as valid_from, 
                     CAST(EXTRACT(EPOCH FROM date_trunc('minute', expires_at)) AS INTEGER) as expires_at", configData)
            .Single();

        // Assert
        var expected = JsonConvert.SerializeObject(new
        {
            configData.key,
            configData.value,
            configData.label,
            configData.content_type,
            valid_from = ((DateTimeOffset)configData.valid_from).ToUnixTimeSeconds(),
            expires_at = ((DateTimeOffset)configData.expires_at).ToUnixTimeSeconds()
        });

        Assert.AreEqual(expected, JsonConvert.SerializeObject(result));
    }
}