using Microsoft.AspNetCore.Mvc;
using Nop.Core;
using Nop.Core.Domain.Orders;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Common;
using Nop.Services.Orders;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
{
    [Area(AreaNames.ADMIN)]
    [AuthorizeAdmin]
    public class InnVoiceOrderSyncController : BasePluginController
    {
        private readonly IOrderService _orderService;
        private readonly IStoreContext _storeContext;
        private readonly InnVoiceOrderService _innVoiceOrderService;
        private readonly IGenericAttributeService _genericAttributeService;

        public InnVoiceOrderSyncController(
            IOrderService _orderService,
            IStoreContext storeContext,
            InnVoiceOrderService innVoiceOrderService,
            IGenericAttributeService genericAttributeService)
        {
            this._orderService = _orderService;
            _storeContext = storeContext;
            _innVoiceOrderService = innVoiceOrderService;
            _genericAttributeService = genericAttributeService;
        }

        /// <summary>
        /// Display the order sync page
        /// </summary>
        [HttpGet]
        public IActionResult Index()
        {
            return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/InnVoiceOrderSync/Index.cshtml");
        }

        /// <summary>
        /// Sync orders between NopCommerce and InnVoice for a given date range
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> SyncOrders(DateTime startDate, DateTime endDate)
        {
            try
            {
                // Validate dates
                if (startDate > endDate)
                {
                    return Json(new { success = false, message = "Start date cannot be after end date" });
                }

                // Get store ID
                var storeId = (await _storeContext.GetCurrentStoreAsync()).Id;

                // Step 1: Get all InnVoice orders for the date range using UpdateTime
                var innVoiceOrders = await GetInnVoiceOrdersByDateRange(startDate, endDate);
                Console.WriteLine($"DEBUG: Found {innVoiceOrders.Count} InnVoice orders");
                Console.WriteLine($"DEBUG: InnVoice TableIds: {string.Join(", ", innVoiceOrders.Select(o => o.TableId))}");

                // Step 2: Get all NopCommerce orders in the date range (filtered by DateOfReceipt)
                var nopOrders = await GetNopOrdersByDateRange(startDate, endDate, storeId);
                Console.WriteLine($"DEBUG: Found {nopOrders.Count} NopCommerce orders with DateOfReceipt in range");

                // Step 3: Get TableIds from NopCommerce orders
                var nopOrderTableIds = new Dictionary<int, int>(); // orderId -> tableId
                var nopOrdersWithoutTableId = new List<int>(); // Track orders without TableId
                
                foreach (var order in nopOrders)
                {
                    var tableIdStr = await _genericAttributeService.GetAttributeAsync<string>(
                        order,
                        "InnVoiceOrderTableId",
                        storeId
                    );

                    if (!string.IsNullOrEmpty(tableIdStr) && int.TryParse(tableIdStr, out int tableId))
                    {
                        nopOrderTableIds[order.Id] = tableId;
                        Console.WriteLine($"DEBUG: Order {order.Id} has TableId {tableId}");
                    }
                    else
                    {
                        nopOrdersWithoutTableId.Add(order.Id);
                        Console.WriteLine($"DEBUG: Order {order.Id} has NO TableId (tableIdStr='{tableIdStr}')");
                    }
                }

                // Step 4: Compare and find discrepancies
                var innVoiceTableIds = new HashSet<int>(innVoiceOrders.Select(o => o.TableId).Where(t => t > 0));
                var nopTableIds = new HashSet<int>(nopOrderTableIds.Values);

                // Orders in InnVoice but not in NopCommerce
                var innVoiceOnly = innVoiceTableIds.Except(nopTableIds).ToList();

                // Orders in NopCommerce but not in InnVoice
                var nopOnly = nopTableIds.Except(innVoiceTableIds).ToList();

                // Orders in both systems
                var inBoth = innVoiceTableIds.Intersect(nopTableIds).ToList();

                // Build detailed results
                var innVoiceOnlyDetails = innVoiceOrders
                    .Where(o => innVoiceOnly.Contains(o.TableId))
                    .Select(o => new
                    {
                        tableId = o.TableId,
                        techId = o.TechId,
                        customerName = o.CustomerName,
                        totalGross = o.TotalGross,
                        orderDate = o.OrderDate,
                        externalOrderNumber = o.ExternalOrderNumber
                    })
                    .ToList();

                var nopOnlyDetails = nopOrders
                    .Where(o => nopOrderTableIds.ContainsKey(o.Id) && nopOnly.Contains(nopOrderTableIds[o.Id]))
                    .Select(o => new
                    {
                        orderId = o.Id,
                        tableId = nopOrderTableIds.ContainsKey(o.Id) ? nopOrderTableIds[o.Id] : 0,
                        customOrderNumber = o.CustomOrderNumber,
                        orderTotal = o.OrderTotal,
                        createdOn = o.CreatedOnUtc
                    })
                    .ToList();

                // Orders without TableId (never uploaded to InnVoice)
                var nopOrdersNotUploadedDetails = new List<dynamic>();
                
                foreach (var order in nopOrders.Where(o => nopOrdersWithoutTableId.Contains(o.Id)))
                {
                    // Check if order has items
                    var orderItems = await _orderService.GetOrderItemsAsync(order.Id);
                    var hasItems = orderItems.Any();
                    
                    nopOrdersNotUploadedDetails.Add(new
                    {
                        orderId = order.Id,
                        customOrderNumber = order.CustomOrderNumber,
                        orderTotal = order.OrderTotal,
                        createdOn = order.CreatedOnUtc,
                        orderStatus = order.OrderStatus.ToString(),
                        orderStatusId = (int)order.OrderStatus,
                        hasItems = hasItems,
                        itemCount = orderItems.Count()
                    });
                }

                // Separate orders by whether they have items
                var deletedOrders = nopOrdersNotUploadedDetails
                    .Where(o => !o.hasItems) // No items = deleted/empty order
                    .ToList();
                
                var missingUploads = nopOrdersNotUploadedDetails
                    .Where(o => o.hasItems) // Has items = should have been uploaded
                    .ToList();

                return Json(new
                {
                    success = true,
                    data = new
                    {
                        summary = new
                        {
                            totalInnVoiceOrders = innVoiceOrders.Count,
                            totalNopOrders = nopOrders.Count,
                            totalNopOrdersWithTableId = nopOrderTableIds.Count,
                            totalNopOrdersWithoutTableId = nopOrdersWithoutTableId.Count,
                            totalDeletedOrders = deletedOrders.Count,
                            totalMissingUploads = missingUploads.Count,
                            inBothSystems = inBoth.Count,
                            onlyInInnVoice = innVoiceOnly.Count,
                            onlyInNopCommerce = nopOnly.Count
                        },
                        innVoiceOnly = innVoiceOnlyDetails,
                        nopCommerceOnly = nopOnlyDetails,
                        nopCommerceNotUploaded = nopOrdersNotUploadedDetails,
                        deletedOrders = deletedOrders,
                        missingUploads = missingUploads,
                        dateRange = new
                        {
                            startDate = startDate.ToString("yyyy-MM-dd"),
                            endDate = endDate.ToString("yyyy-MM-dd")
                        }
                    }
                });
            }
            catch (Exception ex)
            {
                return Json(new
                {
                    success = false,
                    message = $"Error: {ex.Message}"
                });
            }
        }

        /// <summary>
        /// Quick action: Sync last 30 days
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> SyncLast30Days()
        {
            var endDate = DateTime.Now;
            var startDate = endDate.AddDays(-30);

            return await SyncOrders(startDate, endDate);
        }

        /// <summary>
        /// Get InnVoice orders by UpdateTime range
        /// We query by UpdateTime but DON'T filter by MegrendelesKelte
        /// This allows comparing ALL orders regardless of order date mismatches
        /// </summary>
        private async Task<List<InnVoiceOrder>> GetInnVoiceOrdersByDateRange(DateTime startDate, DateTime endDate)
        {
            var allOrders = new List<InnVoiceOrder>();
            
            // Add 14-day buffer to catch orders that might have been modified/created around this time
            //var queryStartDate = startDate.AddDays(-14);
            //var queryEndDate = endDate.AddDays(7);
            //var currentDate = queryStartDate;
            var currentDate = startDate;

            //Console.WriteLine($"DEBUG: Querying InnVoice from {queryStartDate:yyyy-MM-dd} to {queryEndDate:yyyy-MM-dd}");
            Console.WriteLine($"DEBUG: Querying InnVoice from {startDate:yyyy-MM-dd} to {endDate:yyyy-MM-dd}");

            // Query day by day to avoid rate limits
            while (currentDate <= endDate)
            {
                try
                {
                    var orders = await _innVoiceOrderService.GetOrdersByOrderDateAsync(currentDate);

                    if (orders != null && orders.Any())
                    {
                        Console.WriteLine($"DEBUG: Found {orders.Count} orders updated on {currentDate:yyyy-MM-dd}");
                        allOrders.AddRange(orders);
                    }

                    currentDate = currentDate.AddDays(1);
                }
                catch (Exception ex)
                {
                    // Log and continue
                    Console.WriteLine($"Error fetching InnVoice orders for {currentDate:yyyy-MM-dd}: {ex.Message}");
                    currentDate = currentDate.AddDays(1);
                }
            }

            // Remove duplicates based on TableId
            var uniqueOrders = allOrders
                .GroupBy(o => o.TableId)
                .Select(g => g.First())
                .ToList();

            Console.WriteLine($"DEBUG: Total unique InnVoice orders: {uniqueOrders.Count}");

            return uniqueOrders;
        }

        /// <summary>
        /// Get NopCommerce orders by DateOfReceipt generic attribute date range
        /// </summary>
        private async Task<List<Order>> GetNopOrdersByDateRange(DateTime startDate, DateTime endDate, int storeId)
        {
            // Get ALL orders (we'll filter by DateOfReceipt manually)
            var allOrders = await _orderService.SearchOrdersAsync();
            
            var filteredOrders = new List<Order>();

            foreach (var order in allOrders)
            {
                // Get the DateOfReceipt generic attribute
                var dateOfReceiptStr = await _genericAttributeService.GetAttributeAsync<string>(
                    order,
                    "DateOfReceipt",
                    storeId
                );

                // Parse and check if within range
                if (!string.IsNullOrEmpty(dateOfReceiptStr) && DateTime.TryParse(dateOfReceiptStr, out DateTime dateOfReceipt))
                {
                    // Compare dates (ignoring time component)
                    var receiptDate = dateOfReceipt.Date;
                    var start = startDate.Date;
                    var end = endDate.Date;

                    if (receiptDate >= start && receiptDate <= end)
                    {
                        filteredOrders.Add(order);
                    }
                }
            }

            return filteredOrders;
        }
    }
}
