using System.Reflection;
using e_suite.Database.Audit;
using eSuite.Core.Clock;
using Microsoft.EntityFrameworkCore;

namespace e_suite.Database.Core.Helpers;

public class SunDatabaseEntityContext : AuditableEntityContext, IDatabaseCore
{
    public SunDatabaseEntityContext(DbContextOptions options, IClock clock) : base(options, clock)
    {
    }
    
    public virtual Task InitializeAsync()
    {
        return Task.CompletedTask;
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        var type = GetType();

        var publicProperties = type.GetProperties();

        foreach (var property in publicProperties)
        {
            if (!property.PropertyType.IsGenericType)
                continue;

            if (property.PropertyType.GetGenericTypeDefinition() != typeof(DbSet<>))
                continue;

            var table = property.PropertyType.GenericTypeArguments[0];

            var tableMethod = table.GetMethod("OnModelCreating", BindingFlags.Static | BindingFlags.Public);

            if (tableMethod != null)
                tableMethod.Invoke(null, new object?[] { modelBuilder });
        }
    }

    public async Task<T> TransactionAsync<T>(Func<Task<T>> action)
    {
        if (Database.CurrentTransaction != null)
            return await action();

        await using var transaction = await Database.BeginTransactionAsync();

        try
        {
            var result = await action();

            await transaction.CommitAsync();

            return result;
        }
        catch (Exception)
        {
            await transaction.RollbackAsync();
            throw;
        }
    }

    public async Task TransactionAsync(Func<Task> action)
    {
        if (Database.CurrentTransaction != null)
        {
            await action();
            return;
        }

        await using var transaction = await Database.BeginTransactionAsync();

        try
        {
            await action();

            await transaction.CommitAsync();
        }
        catch (Exception)
        {
            await transaction.RollbackAsync();
            throw;
        }
    }
}