using System.Collections.Generic;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Enums;
using CMSMicroservice.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.Infrastructure.Services;

/// <summary>
/// پیاده‌سازی سرویس مدیریت موجودی
/// این سرویس Source of Truth برای موجودی است و مسئول همگام‌سازی با Product.RemainingCount
/// </summary>
public class InventoryService : IInventoryService
{
    private readonly ApplicationDbContext _context;
    private readonly ILogger<InventoryService> _logger;
    private const long DefaultWarehouseId = 1; // انبار پیش‌فرض

    public InventoryService(ApplicationDbContext context, ILogger<InventoryService> logger)
    {
        _context = context;
        _logger = logger;
    }

    #region Initialization

    public async Task<long> InitializeInventoryAsync(
        long productId,
        ProductType productType,
        int initialQuantity,
        long? warehouseId = null,
        int lowStockThreshold = 10,
        CancellationToken ct = default)
    {
        var effectiveWarehouseId = warehouseId ?? DefaultWarehouseId;

        // چک کردن اینکه آیا قبلاً InventoryItem برای این محصول وجود دارد
        var existingItem = productType == ProductType.RegularProduct
            ? await _context.InventoryItems.FirstOrDefaultAsync(
                x => x.ProductId == productId && x.WarehouseId == effectiveWarehouseId && !x.IsDeleted, ct)
            : await _context.InventoryItems.FirstOrDefaultAsync(
                x => x.DiscountProductId == productId && x.WarehouseId == effectiveWarehouseId && !x.IsDeleted, ct);

        if (existingItem != null)
        {
            _logger.LogWarning("InventoryItem already exists for {ProductType} with Id {ProductId}", 
                productType, productId);
            return existingItem.Id;
        }

        // ایجاد InventoryItem جدید
        var inventoryItem = new InventoryItem
        {
            ProductId = productType == ProductType.RegularProduct ? productId : null,
            DiscountProductId = productType == ProductType.DiscountProduct ? productId : null,
            ProductType = productType,
            Quantity = initialQuantity,
            ReservedQuantity = 0,
            LowStockThreshold = lowStockThreshold,
            WarehouseId = effectiveWarehouseId
        };

        _context.InventoryItems.Add(inventoryItem);
        await _context.SaveChangesAsync(ct);

        // ثبت StockMovement اولیه
        if (initialQuantity > 0)
        {
            await LogMovementAsync(
                inventoryItem.Id,
                StockMovementType.InitialStock,
                initialQuantity,
                0,
                initialQuantity,
                null,
                null,
                null,
                "موجودی اولیه",
                null,
                ct);
        }

        // همگام‌سازی با Product.RemainingCount
        await SyncRemainingCountAsync(inventoryItem, ct);

        _logger.LogInformation(
            "Initialized inventory for {ProductType} Id={ProductId}, Quantity={Quantity}",
            productType, productId, initialQuantity);

        return inventoryItem.Id;
    }

    #endregion

    #region Query Operations

    public async Task<InventoryItem?> GetInventoryAsync(
        long productId,
        ProductType productType,
        long? warehouseId = null,
        CancellationToken ct = default)
    {
        var effectiveWarehouseId = warehouseId ?? DefaultWarehouseId;

        return productType == ProductType.RegularProduct
            ? await _context.InventoryItems.FirstOrDefaultAsync(
                x => x.ProductId == productId && x.WarehouseId == effectiveWarehouseId && !x.IsDeleted, ct)
            : await _context.InventoryItems.FirstOrDefaultAsync(
                x => x.DiscountProductId == productId && x.WarehouseId == effectiveWarehouseId && !x.IsDeleted, ct);
    }

    public async Task<int> GetAvailableQuantityAsync(
        long productId,
        ProductType productType,
        long? warehouseId = null,
        CancellationToken ct = default)
    {
        var item = await GetInventoryAsync(productId, productType, warehouseId, ct);
        return item?.AvailableQuantity ?? 0;
    }

    public async Task<bool> CheckAvailabilityAsync(
        long productId,
        ProductType productType,
        int requiredQuantity,
        long? warehouseId = null,
        CancellationToken ct = default)
    {
        var available = await GetAvailableQuantityAsync(productId, productType, warehouseId, ct);
        return available >= requiredQuantity;
    }

    public async Task<List<InventoryItem>> GetLowStockItemsAsync(
        ProductType? productType = null,
        long? warehouseId = null,
        int count = 50,
        CancellationToken ct = default)
    {
        var query = _context.InventoryItems
            .Where(x => !x.IsDeleted)
            .Where(x => x.Quantity <= x.LowStockThreshold);

        if (productType.HasValue)
            query = query.Where(x => x.ProductType == productType.Value);

        if (warehouseId.HasValue)
            query = query.Where(x => x.WarehouseId == warehouseId.Value);

        return await query
            .OrderBy(x => x.Quantity)
            .Take(count)
            .ToListAsync(ct);
    }

    public async Task<List<StockMovement>> GetStockMovementsAsync(
        long productId,
        ProductType productType,
        DateTime? fromDate = null,
        DateTime? toDate = null,
        CancellationToken ct = default)
    {
        var inventoryItem = await GetInventoryAsync(productId, productType, null, ct);
        if (inventoryItem == null)
            return new List<StockMovement>();

        var query = _context.StockMovements
            .Where(x => x.InventoryItemId == inventoryItem.Id && !x.IsDeleted);

        if (fromDate.HasValue)
            query = query.Where(x => x.Created >= fromDate.Value);

        if (toDate.HasValue)
            query = query.Where(x => x.Created <= toDate.Value);

        return await query
            .OrderByDescending(x => x.Created)
            .ToListAsync(ct);
    }

    #endregion

    #region Order Flow Operations

    public async Task<bool> ReserveStockAsync(
        long productId,
        ProductType productType,
        int quantity,
        long? orderId = null,
        CancellationToken ct = default)
    {
        var item = await GetInventoryAsync(productId, productType, null, ct);
        if (item == null)
        {
            _logger.LogWarning("Cannot reserve: InventoryItem not found for {ProductType} Id={ProductId}",
                productType, productId);
            return false;
        }

        if (item.AvailableQuantity < quantity)
        {
            _logger.LogWarning(
                "Cannot reserve: Insufficient stock. Available={Available}, Requested={Requested}",
                item.AvailableQuantity, quantity);
            return false;
        }

        var quantityBefore = item.ReservedQuantity;
        item.ReservedQuantity += quantity;

        await _context.SaveChangesAsync(ct);

        // ثبت StockMovement
        await LogMovementAsync(
            item.Id,
            StockMovementType.Reserved,
            quantity,
            quantityBefore,
            item.ReservedQuantity,
            orderId,
            productType == ProductType.DiscountProduct ? orderId : null,
            null,
            "رزرو برای سفارش",
            null,
            ct);

        _logger.LogInformation(
            "Reserved {Quantity} units for {ProductType} Id={ProductId}, OrderId={OrderId}",
            quantity, productType, productId, orderId);

        return true;
    }

    public async Task<bool> ReleaseReservationAsync(
        long productId,
        ProductType productType,
        int quantity,
        long? orderId = null,
        CancellationToken ct = default)
    {
        var item = await GetInventoryAsync(productId, productType, null, ct);
        if (item == null)
        {
            _logger.LogWarning("Cannot release: InventoryItem not found for {ProductType} Id={ProductId}",
                productType, productId);
            return false;
        }

        var quantityBefore = item.ReservedQuantity;
        item.ReservedQuantity = Math.Max(0, item.ReservedQuantity - quantity);
        var releasedQuantity = quantityBefore - item.ReservedQuantity;

        await _context.SaveChangesAsync(ct);

        if (releasedQuantity == 0)
        {
            _logger.LogInformation(
                "No reserved units to release for {ProductType} Id={ProductId}, OrderId={OrderId}",
                productType, productId, orderId);
            return true;
        }

        // ثبت StockMovement
        await LogMovementAsync(
            item.Id,
            StockMovementType.Released,
            -releasedQuantity,
            quantityBefore,
            item.ReservedQuantity,
            orderId,
            productType == ProductType.DiscountProduct ? orderId : null,
            null,
            "آزادسازی رزرو",
            null,
            ct);

        _logger.LogInformation(
            "Released {Quantity} reserved units for {ProductType} Id={ProductId}, OrderId={OrderId}",
            releasedQuantity, productType, productId, orderId);

        return true;
    }

    public async Task<bool> ConfirmSaleAsync(
        long productId,
        ProductType productType,
        int quantity,
        long? orderId = null,
        CancellationToken ct = default)
    {
        var item = await GetInventoryAsync(productId, productType, null, ct);
        if (item == null)
        {
            _logger.LogWarning("Cannot confirm sale: InventoryItem not found for {ProductType} Id={ProductId}",
                productType, productId);
            return false;
        }

        var quantityBefore = item.Quantity;

        // کاهش موجودی واقعی
        item.Quantity -= quantity;
        
        // کاهش رزرو (اگر رزرو شده بود)
        item.ReservedQuantity = Math.Max(0, item.ReservedQuantity - quantity);
        
        // آپدیت آخرین فروش
        item.LastSoldAt = DateTime.UtcNow;

        await _context.SaveChangesAsync(ct);

        // ثبت StockMovement
        await LogMovementAsync(
            item.Id,
            StockMovementType.Sale,
            -quantity, // منفی برای خروج
            quantityBefore,
            item.Quantity,
            orderId,
            productType == ProductType.DiscountProduct ? orderId : null,
            null,
            "فروش",
            null,
            ct);

        // همگام‌سازی با Product.RemainingCount
        await SyncRemainingCountAsync(item, ct);

        _logger.LogInformation(
            "Confirmed sale of {Quantity} units for {ProductType} Id={ProductId}, OrderId={OrderId}. New Quantity={NewQuantity}",
            quantity, productType, productId, orderId, item.Quantity);

        return true;
    }

    #endregion

    #region Stock Management Operations

    public async Task<bool> AddStockAsync(
        long productId,
        ProductType productType,
        int quantity,
        string? referenceNumber = null,
        string? note = null,
        long? performedByUserId = null,
        CancellationToken ct = default)
    {
        var item = await GetInventoryAsync(productId, productType, null, ct);
        if (item == null)
        {
            _logger.LogWarning("Cannot add stock: InventoryItem not found for {ProductType} Id={ProductId}",
                productType, productId);
            return false;
        }

        var quantityBefore = item.Quantity;
        item.Quantity += quantity;
        item.LastRestockedAt = DateTime.UtcNow;

        await _context.SaveChangesAsync(ct);

        // ثبت StockMovement
        await LogMovementAsync(
            item.Id,
            StockMovementType.Restock,
            quantity,
            quantityBefore,
            item.Quantity,
            null,
            null,
            referenceNumber,
            note ?? "ورود کالا",
            performedByUserId,
            ct);

        // همگام‌سازی با Product.RemainingCount
        await SyncRemainingCountAsync(item, ct);

        _logger.LogInformation(
            "Added {Quantity} units to {ProductType} Id={ProductId}. New Quantity={NewQuantity}",
            quantity, productType, productId, item.Quantity);

        return true;
    }

    public async Task<bool> AdjustStockAsync(
        long productId,
        ProductType productType,
        int newQuantity,
        string? note = null,
        long? performedByUserId = null,
        CancellationToken ct = default)
    {
        var item = await GetInventoryAsync(productId, productType, null, ct);
        if (item == null)
        {
            _logger.LogWarning("Cannot adjust stock: InventoryItem not found for {ProductType} Id={ProductId}",
                productType, productId);
            return false;
        }

        var quantityBefore = item.Quantity;
        var difference = newQuantity - quantityBefore;

        item.Quantity = newQuantity;

        await _context.SaveChangesAsync(ct);

        // ثبت StockMovement
        var movementType = difference >= 0 
            ? StockMovementType.AdjustmentPlus 
            : StockMovementType.AdjustmentMinus;

        await LogMovementAsync(
            item.Id,
            movementType,
            difference,
            quantityBefore,
            newQuantity,
            null,
            null,
            null,
            note ?? "تعدیل موجودی",
            performedByUserId,
            ct);

        // همگام‌سازی با Product.RemainingCount
        await SyncRemainingCountAsync(item, ct);

        _logger.LogInformation(
            "Adjusted stock for {ProductType} Id={ProductId}. Before={Before}, After={After}",
            productType, productId, quantityBefore, newQuantity);

        return true;
    }

    public async Task<bool> ProcessReturnAsync(
        long productId,
        ProductType productType,
        int quantity,
        long? orderId = null,
        string? note = null,
        long? performedByUserId = null,
        CancellationToken ct = default)
    {
        var item = await GetInventoryAsync(productId, productType, null, ct);
        if (item == null)
        {
            _logger.LogWarning("Cannot process return: InventoryItem not found for {ProductType} Id={ProductId}",
                productType, productId);
            return false;
        }

        var quantityBefore = item.Quantity;
        item.Quantity += quantity;

        await _context.SaveChangesAsync(ct);

        // ثبت StockMovement
        await LogMovementAsync(
            item.Id,
            StockMovementType.Return,
            quantity,
            quantityBefore,
            item.Quantity,
            orderId,
            productType == ProductType.DiscountProduct ? orderId : null,
            null,
            note ?? "برگشت از مشتری",
            performedByUserId,
            ct);

        // همگام‌سازی با Product.RemainingCount
        await SyncRemainingCountAsync(item, ct);

        _logger.LogInformation(
            "Processed return of {Quantity} units for {ProductType} Id={ProductId}. New Quantity={NewQuantity}",
            quantity, productType, productId, item.Quantity);

        return true;
    }

    public async Task<bool> RecordLossAsync(
        long productId,
        ProductType productType,
        int quantity,
        StockMovementType lossType,
        string? note = null,
        long? performedByUserId = null,
        CancellationToken ct = default)
    {
        if (lossType != StockMovementType.Damaged && lossType != StockMovementType.Lost)
        {
            _logger.LogWarning("Invalid loss type: {LossType}. Must be Damaged or Lost.", lossType);
            return false;
        }

        var item = await GetInventoryAsync(productId, productType, null, ct);
        if (item == null)
        {
            _logger.LogWarning("Cannot record loss: InventoryItem not found for {ProductType} Id={ProductId}",
                productType, productId);
            return false;
        }

        var quantityBefore = item.Quantity;
        item.Quantity = Math.Max(0, item.Quantity - quantity);

        await _context.SaveChangesAsync(ct);

        // ثبت StockMovement
        await LogMovementAsync(
            item.Id,
            lossType,
            -quantity,
            quantityBefore,
            item.Quantity,
            null,
            null,
            null,
            note ?? (lossType == StockMovementType.Damaged ? "ضایعات" : "مفقودی"),
            performedByUserId,
            ct);

        // همگام‌سازی با Product.RemainingCount
        await SyncRemainingCountAsync(item, ct);

        _logger.LogInformation(
            "Recorded {LossType} of {Quantity} units for {ProductType} Id={ProductId}. New Quantity={NewQuantity}",
            lossType, quantity, productType, productId, item.Quantity);

        return true;
    }

    #endregion

    #region Bulk Operations

    public async Task<bool> BulkReserveStockAsync(
        IEnumerable<(long ProductId, ProductType ProductType, int Quantity)> items,
        long? orderId = null,
        CancellationToken ct = default)
    {
        foreach (var (productId, productType, quantity) in items)
        {
            var result = await ReserveStockAsync(productId, productType, quantity, orderId, ct);
            if (!result)
            {
                // در صورت خطا، رزروهای قبلی را آزاد کنید
                _logger.LogError(
                    "Bulk reserve failed at {ProductType} Id={ProductId}. Rolling back...",
                    productType, productId);
                // TODO: Implement rollback logic
                return false;
            }
        }
        return true;
    }

    public async Task<bool> BulkReleaseReservationAsync(
        IEnumerable<(long ProductId, ProductType ProductType, int Quantity)> items,
        long? orderId = null,
        CancellationToken ct = default)
    {
        foreach (var (productId, productType, quantity) in items)
        {
            await ReleaseReservationAsync(productId, productType, quantity, orderId, ct);
        }
        return true;
    }

    public async Task<bool> BulkConfirmSaleAsync(
        IEnumerable<(long ProductId, ProductType ProductType, int Quantity)> items,
        long? orderId = null,
        CancellationToken ct = default)
    {
        foreach (var (productId, productType, quantity) in items)
        {
            var result = await ConfirmSaleAsync(productId, productType, quantity, orderId, ct);
            if (!result)
            {
                _logger.LogError(
                    "Bulk confirm sale failed at {ProductType} Id={ProductId}",
                    productType, productId);
                return false;
            }
        }
        return true;
    }

    #endregion

    #region Private Helper Methods

    /// <summary>
    /// همگام‌سازی موجودی InventoryItem با Product.RemainingCount
    /// این متد اطمینان می‌دهد که داده‌های قدیمی (RemainingCount) همیشه با سیستم جدید sync است
    /// </summary>
    private async Task SyncRemainingCountAsync(InventoryItem item, CancellationToken ct)
    {
        try
        {
            if (item.ProductType == ProductType.RegularProduct && item.ProductId.HasValue)
            {
                var product = await _context.Products.FindAsync(new object[] { item.ProductId.Value }, ct);
                if (product != null)
                {
                    product.RemainingCount = item.Quantity;
                    await _context.SaveChangesAsync(ct);
                    _logger.LogDebug("Synced RemainingCount for Product Id={ProductId} to {Quantity}",
                        item.ProductId, item.Quantity);
                }
            }
            else if (item.ProductType == ProductType.DiscountProduct && item.DiscountProductId.HasValue)
            {
                var discountProduct = await _context.DiscountProducts.FindAsync(
                    new object[] { item.DiscountProductId.Value }, ct);
                if (discountProduct != null)
                {
                    discountProduct.RemainingCount = item.Quantity;
                    await _context.SaveChangesAsync(ct);
                    _logger.LogDebug("Synced RemainingCount for DiscountProduct Id={ProductId} to {Quantity}",
                        item.DiscountProductId, item.Quantity);
                }
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to sync RemainingCount for InventoryItem Id={ItemId}", item.Id);
            // Don't throw - sync failure shouldn't break the main operation
        }
    }

    /// <summary>
    /// ثبت حرکت موجودی در StockMovements
    /// </summary>
    private async Task LogMovementAsync(
        long inventoryItemId,
        StockMovementType movementType,
        int quantity,
        int quantityBefore,
        int quantityAfter,
        long? orderId,
        long? discountOrderId,
        string? referenceNumber,
        string? note,
        long? performedByUserId,
        CancellationToken ct)
    {
        var movement = new StockMovement
        {
            InventoryItemId = inventoryItemId,
            MovementType = movementType,
            Quantity = quantity,
            QuantityBefore = quantityBefore,
            QuantityAfter = quantityAfter,
            OrderId = orderId,
            DiscountOrderId = discountOrderId,
            ReferenceNumber = referenceNumber,
            Note = note,
            PerformedByUserId = performedByUserId
        };

        _context.StockMovements.Add(movement);
        await _context.SaveChangesAsync(ct);
    }

    #endregion
}
