using System.Collections.Generic;
using System.Linq;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Common;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.Infrastructure.Persistence.Interceptors;

/// <summary>
/// Interceptor ثبت خودکار تاریخچه تغییرات (Q27).
/// هر entity که IHasHistory&lt;T&gt; رو implement کرده باشه،
/// هنگام Modified یا Added شدن یک رکورد history ثبت می‌شه.
/// </summary>
public class HistoryTrackingSaveChangesInterceptor : SaveChangesInterceptor
{
    private readonly ICurrentUserService _currentUserService;
    private readonly ILogger<HistoryTrackingSaveChangesInterceptor> _logger;

    public HistoryTrackingSaveChangesInterceptor(
        ICurrentUserService currentUserService,
        ILogger<HistoryTrackingSaveChangesInterceptor> logger)
    {
        _currentUserService = currentUserService;
        _logger = logger;
    }

    public override InterceptionResult<int> SavingChanges(
        DbContextEventData eventData,
        InterceptionResult<int> result)
    {
        TrackHistoryChanges(eventData.Context);
        return base.SavingChanges(eventData, result);
    }

    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
        DbContextEventData eventData,
        InterceptionResult<int> result,
        CancellationToken cancellationToken = default)
    {
        TrackHistoryChanges(eventData.Context);
        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }

    private void TrackHistoryChanges(DbContext? context)
    {
        if (context == null) return;

        var performedBy = _currentUserService.GetPerformedBy();
        var historyEntries = new List<object>();

        foreach (var entry in context.ChangeTracker.Entries())
        {
            // فقط Modified و Added — Deleted رو ignore می‌کنیم (soft delete)
            if (entry.State != EntityState.Modified && entry.State != EntityState.Added)
                continue;

            var entityType = entry.Entity.GetType();

            // بررسی اینکه entity آیا IHasHistory<T> رو implement کرده
            var historyInterface = entityType.GetInterfaces()
                .FirstOrDefault(i => i.IsGenericType
                                     && i.GetGenericTypeDefinition() == typeof(IHasHistory<>));

            if (historyInterface == null)
                continue;

            // تعیین نوع عملیات
            var action = entry.State == EntityState.Added ? "Created" : "Updated";

            try
            {
                // فراخوانی CreateHistorySnapshot از طریق reflection
                var method = historyInterface.GetMethod("CreateHistorySnapshot");
                if (method == null) continue;

                var historyEntity = method.Invoke(entry.Entity, new object?[] { action, performedBy });
                if (historyEntity == null) continue;

                // برای Modified: مقادیر Original رو ست کنیم
                if (entry.State == EntityState.Modified)
                {
                    SetOriginalValues(entry, historyEntity);
                }

                historyEntries.Add(historyEntity);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex,
                    "Failed to create history snapshot for {EntityType} (Id={Id})",
                    entityType.Name,
                    entry.Property("Id").CurrentValue);
            }
        }

        // اضافه کردن history entities به context
        foreach (var historyEntry in historyEntries)
        {
            context.Add(historyEntry);
        }

        if (historyEntries.Count > 0)
        {
            _logger.LogDebug("HistoryTrackingInterceptor: {Count} history records queued", historyEntries.Count);
        }
    }

    /// <summary>
    /// ست کردن مقادیر Original (قبل از تغییر) روی history entity.
    /// از روی نام property: "OldX" ← OriginalValue("X"), "NewX" ← CurrentValue("X")
    /// </summary>
    private static void SetOriginalValues(
        Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry entry,
        object historyEntity)
    {
        var historyType = historyEntity.GetType();
        var historyProps = historyType.GetProperties();

        foreach (var historyProp in historyProps)
        {
            // الگو: OldPrice ← entry.OriginalValues["Price"]
            if (!historyProp.Name.StartsWith("Old") || !historyProp.CanWrite)
                continue;

            var sourcePropertyName = historyProp.Name[3..]; // "OldPrice" → "Price"

            try
            {
                var entryProperty = entry.Properties
                    .FirstOrDefault(p => p.Metadata.Name == sourcePropertyName);

                if (entryProperty != null)
                {
                    var originalValue = entryProperty.OriginalValue;
                    // تبدیل نوع اگه nullable باشه
                    if (originalValue != null || Nullable.GetUnderlyingType(historyProp.PropertyType) != null)
                    {
                        historyProp.SetValue(historyEntity, originalValue);
                    }
                }
            }
            catch
            {
                // اگه property match نداشت، skip — ممکنه OldIsActive با bool? باشه
            }
        }
    }
}
