using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Nop.Core;
using Nop.Core.Domain.Orders;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Catalog;
using Nop.Services.Customers;
using Nop.Services.Localization;
using Nop.Services.Orders;
using Nop.Web.Framework.Controllers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
{
    [AutoValidateAntiforgeryToken]
    public class QuickOrderController : BasePluginController
    {
        private readonly IWorkContext _workContext;
        private readonly IStoreContext _storeContext;
        private readonly IProductService _productService;
        private readonly IShoppingCartService _shoppingCartService;
        private readonly ICustomerService _customerService;
        private readonly ILocalizationService _localizationService;
        private readonly CustomPriceCalculationService _customPriceCalculationService;
        private readonly OpenAIApiService _aiApiService;
        private readonly CerebrasAPIService _cerebrasApiService;
        private readonly FruitBankDbContext _dbContext;
        private readonly FruitBankAttributeService _fruitBankAttributeService;

        private const string PendingDeliveryDateTimeKey = "QuickOrderPendingDeliveryDateTime";

        // Resource key prefix
        private const string Prefix = "Plugins.Misc.FruitBankPlugin.QuickOrder.";

        public QuickOrderController(
            IWorkContext workContext,
            IStoreContext storeContext,
            IProductService productService,
            IShoppingCartService shoppingCartService,
            ICustomerService customerService,
            ILocalizationService localizationService,
            IPriceCalculationService priceCalculationService,
            OpenAIApiService aiApiService,
            CerebrasAPIService cerebrasApiService,
            FruitBankDbContext dbContext,
            FruitBankAttributeService fruitBankAttributeService)
        {
            _workContext = workContext;
            _storeContext = storeContext;
            _productService = productService;
            _shoppingCartService = shoppingCartService;
            _customerService = customerService;
            _localizationService = localizationService;
            _customPriceCalculationService = priceCalculationService as CustomPriceCalculationService;
            _aiApiService = aiApiService;
            _cerebrasApiService = cerebrasApiService;
            _dbContext = dbContext;
            _fruitBankAttributeService = fruitBankAttributeService;
        }

        [HttpGet]
        public async Task<IActionResult> Index()
        {
            var customer = await _workContext.GetCurrentCustomerAsync();
            if (await _customerService.IsGuestAsync(customer))
                return Challenge();

            return View("~/Plugins/Misc.FruitBankPlugin/Views/QuickOrder/Index.cshtml");
        }

        /// <summary>
        /// Return the previously saved delivery datetime for this customer, if any.
        /// Used on page load to restore state when the customer revisits or opens a new tab.
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> GetDeliveryDateTime()
        {
            var customer = await _workContext.GetCurrentCustomerAsync();
            if (await _customerService.IsGuestAsync(customer))
                return Json(new { success = false });

            var store = await _storeContext.GetCurrentStoreAsync();
            var saved = await _fruitBankAttributeService
                .GetGenericAttributeValueAsync<Nop.Core.Domain.Customers.Customer, DateTime?>(
                    customer.Id, PendingDeliveryDateTimeKey, store.Id);

            if (!saved.HasValue)
                return Json(new { success = true, hasValue = false });

            return Json(new
            {
                success = true,
                hasValue = true,
                date = saved.Value.ToString("yyyy-MM-dd"),
                time = saved.Value.ToString("HH:mm"),
                iso  = saved.Value.ToString("yyyy-MM-ddTHH:mm")
            });
        }

        /// <summary>
        /// Return all available products with prices, optionally filtered by delivery date/slot.
        /// </summary>
        /// <summary>
        /// Save the customer's chosen delivery date+time as a generic attribute.
        /// The OrderPlacedEvent handler will transfer it to the order as DateOfReceipt.
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> SetDeliveryDateTime(string deliveryDateTime)
        {
            var customer = await _workContext.GetCurrentCustomerAsync();
            if (await _customerService.IsGuestAsync(customer))
                return Json(new { success = false, message = await L("NotLoggedIn") });

            if (string.IsNullOrWhiteSpace(deliveryDateTime))
                return Json(new { success = false, message = await L("NoDeliveryDateTimeProvided") });

            if (!DateTime.TryParse(deliveryDateTime, out var parsedDateTime))
                return Json(new { success = false, message = await L("InvalidDeliveryDateTime") });

            var store = await _storeContext.GetCurrentStoreAsync();
            await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync<Nop.Core.Domain.Customers.Customer, DateTime>(
                customer.Id, PendingDeliveryDateTimeKey, parsedDateTime, store.Id);

            Console.WriteLine($"[QuickOrder] SetDeliveryDateTime – customerId={customer.Id}, dateTime={parsedDateTime:u}");
            return Json(new { success = true });
        }

        [HttpGet]
        public async Task<IActionResult> GetAllProducts(string deliveryDate = null, string deliveryTime = null)
        {
            var customer = await _workContext.GetCurrentCustomerAsync();
            if (await _customerService.IsGuestAsync(customer))
                return Json(new { success = false, message = await L("NotLoggedIn") });

            try
            {
                Console.WriteLine($"[QuickOrder] GetAllProducts – deliveryDate={deliveryDate}, time={deliveryTime}");

                var store = await _storeContext.GetCurrentStoreAsync();
                var allProductDtos = (await _dbContext.ProductDtos.GetAll(true).ToListAsync())
                    .Where(pd => pd.AvailableQuantity > 0);

                // TODO: filter allProductDtos by deliveryDate + deliverySlot once
                //       availability data model is defined (e.g. scheduled stock, delivery windows).

                var result = new List<object>();

                foreach (var product in allProductDtos)
                {
                    var productDto = allProductDtos.FirstOrDefault(x => x.Id == product.Id);
                    if (productDto == null) continue;

                    var availableQty = product.StockQuantity + productDto.IncomingQuantity;
                    if (availableQty <= 0) continue;

                    decimal? unitPrice = null;
                    if (!productDto.IsMeasurable)
                    {
                        var tproduct = await _productService.GetProductByIdAsync(productDto.Id);
                        var priceResult = await _customPriceCalculationService.GetFinalPriceAsync(
                            tproduct, customer, store, null, 0, true, 1, null, null);
                        unitPrice = priceResult.finalPrice;
                    }

                    result.Add(new
                    {
                        id = product.Id,
                        name = product.Name,
                        quantity = 1,
                        requestedQuantity = 1,
                        unitPrice,
                        stockQuantity = availableQty,
                        searchTerm = (string)null,
                        isQuantityReduced = false,
                        isMeasurable = productDto.IsMeasurable
                    });
                }

                return Json(new { success = true, products = result });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[QuickOrder] GetAllProducts error: {ex.Message}");
                return Json(new { success = false, message = $"Hiba: {ex.Message}" });
            }
        }

        /// <summary>
        /// Parse a manually typed product list and return matching products with prices,
        /// optionally filtered by delivery date/slot.
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> SearchProducts(string text, string deliveryDate = null, string deliveryTime = null)
        {
            var customer = await _workContext.GetCurrentCustomerAsync();
            if (await _customerService.IsGuestAsync(customer))
                return Json(new { success = false, message = await L("NotLoggedIn") });

            if (string.IsNullOrWhiteSpace(text))
                return Json(new { success = false, message = await L("NoTextProvided") });

            Console.WriteLine($"[QuickOrder] SearchProducts – deliveryDate={deliveryDate}, time={deliveryTime}");

            // TODO: pass deliveryDate + deliverySlot to EnrichProductData when availability filtering is implemented.

            try
            {
                var parsedProducts = await ParseProductsFromText(text);
                if (parsedProducts == null || parsedProducts.Count == 0)
                    return Json(new { success = false, message = await L("NoProductsIdentified"), transcription = text });

                var store = await _storeContext.GetCurrentStoreAsync();
                var enrichedProducts = await EnrichProductData(parsedProducts, customer, store);

                return Json(new { success = true, transcription = text, products = enrichedProducts });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[QuickOrder] SearchProducts error: {ex.Message}");
                return Json(new { success = false, message = $"Hiba: {ex.Message}" });
            }
        }

        /// <summary>
        /// Transcribe voice audio (Hungarian) then parse and match products,
        /// optionally filtered by delivery date/slot.
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> TranscribeAndSearch(IFormFile audioFile, string deliveryDate = null, string deliveryTime = null)
        {
            var customer = await _workContext.GetCurrentCustomerAsync();
            if (await _customerService.IsGuestAsync(customer))
                return Json(new { success = false, message = await L("NotLoggedIn") });

            if (audioFile == null || audioFile.Length == 0)
                return Json(new { success = false, message = await L("NoAudioReceived") });

            Console.WriteLine($"[QuickOrder] TranscribeAndSearch – deliveryDate={deliveryDate}, time={deliveryTime}");

            // TODO: pass deliveryDate + deliverySlot to EnrichProductData when availability filtering is implemented.

            try
            {
                var transcribedText = await TranscribeAudioFile(audioFile, "hu");
                if (string.IsNullOrEmpty(transcribedText))
                    return Json(new { success = false, message = await L("TranscriptionFailed") });

                Console.WriteLine($"[QuickOrder] Transcription: {transcribedText}");

                var parsedProducts = await ParseProductsFromText(transcribedText);
                if (parsedProducts == null || parsedProducts.Count == 0)
                    return Json(new { success = false, message = await L("NoProductsIdentified"), transcription = transcribedText });

                var store = await _storeContext.GetCurrentStoreAsync();
                var enrichedProducts = await EnrichProductData(parsedProducts, customer, store);

                return Json(new { success = true, transcription = transcribedText, products = enrichedProducts });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[QuickOrder] TranscribeAndSearch error: {ex.Message}");
                return Json(new { success = false, message = $"Hiba: {ex.Message}" });
            }
        }

        /// <summary>
        /// Add a product to the current customer's shopping cart and return the updated cart.
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> AddToCart(int productId, int quantity)
        {
            var customer = await _workContext.GetCurrentCustomerAsync();
            if (await _customerService.IsGuestAsync(customer))
                return Json(new { success = false, message = await L("NotLoggedIn") });

            if (productId <= 0 || quantity <= 0)
                return Json(new { success = false, message = await L("InvalidProductOrQuantity") });

            try
            {
                var product = await _productService.GetProductByIdAsync(productId);
                if (product == null || product.Deleted || !product.Published)
                    return Json(new { success = false, message = await L("ProductNotAvailable") });

                var store = await _storeContext.GetCurrentStoreAsync();

                var warnings = await _shoppingCartService.AddToCartAsync(
                    customer: customer,
                    product: product,
                    shoppingCartType: ShoppingCartType.ShoppingCart,
                    storeId: store.Id,
                    quantity: quantity);

                if (warnings.Any())
                    return Json(new { success = false, message = string.Join("; ", warnings) });

                var cartItems = await GetCartItemsJson(customer, store);
                return Json(new { success = true, cartItems });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[QuickOrder] AddToCart error: {ex.Message}");
                return Json(new { success = false, message = $"Hiba: {ex.Message}" });
            }
        }

        /// <summary>
        /// Return the current customer's cart as JSON (for cart panel refresh).
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> GetCartItems()
        {
            var customer = await _workContext.GetCurrentCustomerAsync();
            if (await _customerService.IsGuestAsync(customer))
                return Json(new { success = false });

            var store = await _storeContext.GetCurrentStoreAsync();
            var cartItems = await GetCartItemsJson(customer, store);
            return Json(new { success = true, cartItems });
        }

        #region Private helpers

        /// <summary>Shorthand: get a localized QuickOrder resource string</summary>
        private Task<string> L(string keySuffix)
            => _localizationService.GetResourceAsync(Prefix + keySuffix);

        private async Task<string> TranscribeAudioFile(IFormFile audioFile, string language)
        {
            var fileName = $"quick_order_{DateTime.Now:yyyyMMdd_HHmmss}.webm";
            var uploadsFolder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "voice");

            if (!Directory.Exists(uploadsFolder))
                Directory.CreateDirectory(uploadsFolder);

            var filePath = Path.Combine(uploadsFolder, fileName);
            using (var stream = new FileStream(filePath, FileMode.Create))
                await audioFile.CopyToAsync(stream);

            string transcribedText;
            using (var audioStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                transcribedText = await _aiApiService.TranscribeAudioAsync(audioStream, fileName, language, null);

            if (!string.IsNullOrEmpty(transcribedText) &&
                (transcribedText.EndsWith(".") || transcribedText.EndsWith("!") || transcribedText.EndsWith("?")))
                transcribedText = transcribedText[..^1];

            try { System.IO.File.Delete(filePath); } catch { /* ignore cleanup errors */ }
            return transcribedText;
        }

        private async Task<List<ParsedProduct>> ParseProductsFromText(string text)
        {
            var systemPrompt = @"You are a product parser for a Hungarian fruit and vegetable wholesale company.
Parse the product names and quantities from the user's input.

CRITICAL RULES:
1. Extract product names and quantities from ANY produce item
2. Normalize product names to singular, lowercase (e.g., 'narancsok' → 'narancs')
3. Handle Hungarian number words ('száz' = 100, 'ötven' = 50, 'húsz' = 20, 'tíz' = 10, 'öt' = 5, 'egy' = 1)
4. Fix common transcription/typing errors (e.g., 'datója' → 'datolya', 'szűlő' → 'szőlő', 'mondarin' → 'mandarin')
5. Return ONLY valid JSON array, no explanations
6. DO NOT include units - only product name and quantity as a number
7. ALWAYS return at least one product if you can parse anything from the input

OUTPUT FORMAT (JSON only):
[
  {""product"": ""narancs"", ""quantity"": 100},
  {""product"": ""alma"", ""quantity"": 50}
]";

            var aiResponse = await _cerebrasApiService.GetSimpleResponseAsync(systemPrompt, $"Parse this: {text}");
            Console.WriteLine($"[QuickOrder] AI parse response: {aiResponse}");

            var jsonMatch = Regex.Match(aiResponse, @"\[.*\]", RegexOptions.Singleline);
            if (!jsonMatch.Success) return new List<ParsedProduct>();

            try
            {
                return System.Text.Json.JsonSerializer.Deserialize<List<ParsedProduct>>(jsonMatch.Value)
                       ?? new List<ParsedProduct>();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[QuickOrder] JSON parse error: {ex.Message}");
                return new List<ParsedProduct>();
            }
        }

        private async Task<List<object>> EnrichProductData(
            List<ParsedProduct> parsedProducts,
            Nop.Core.Domain.Customers.Customer customer,
            Nop.Core.Domain.Stores.Store store)
        {
            var enrichedProducts = new List<object>();
            var allProductDtos = await _dbContext.ProductDtos.GetAll(true).ToListAsync();

            foreach (var parsed in parsedProducts)
            {
                var dbProducts = await _productService.SearchProductsAsync(
                    keywords: parsed.Product,
                    pageIndex: 0,
                    pageSize: 20);

                if (!dbProducts.Any())
                {
                    Console.WriteLine($"[QuickOrder] No products found for: {parsed.Product}");
                    continue;
                }

                foreach (var product in dbProducts)
                {
                    var productDto = allProductDtos.FirstOrDefault(x => x.Id == product.Id);
                    if (productDto == null) continue;

                    var availableQty = product.StockQuantity + productDto.IncomingQuantity;
                    if (availableQty <= 0) continue;

                    var requestedQty = parsed.Quantity;
                    var finalQty = Math.Min(requestedQty, availableQty);
                    var isReduced = finalQty < requestedQty;

                    decimal? unitPrice = null;
                    if (!productDto.IsMeasurable)
                    {
                        var priceResult = await _customPriceCalculationService.GetFinalPriceAsync(
                            product, customer, store, null, 0, true, finalQty, null, null);
                        unitPrice = priceResult.finalPrice;
                    }

                    enrichedProducts.Add(new
                    {
                        id = product.Id,
                        name = product.Name,
                        quantity = finalQty,
                        requestedQuantity = requestedQty,
                        unitPrice,
                        stockQuantity = availableQty,
                        searchTerm = parsed.Product,
                        isQuantityReduced = isReduced,
                        isMeasurable = productDto.IsMeasurable
                    });
                }
            }

            Console.WriteLine($"[QuickOrder] Enriched product count: {enrichedProducts.Count}");
            return enrichedProducts;
        }

        private async Task<List<object>> GetCartItemsJson(
            Nop.Core.Domain.Customers.Customer customer,
            Nop.Core.Domain.Stores.Store store)
        {
            var cart = await _shoppingCartService.GetShoppingCartAsync(
                customer, ShoppingCartType.ShoppingCart, store.Id);

            var allProductDtos = await _dbContext.ProductDtos.GetAll(true).ToListAsync();
            var result = new List<object>();

            foreach (var item in cart)
            {
                var product = await _productService.GetProductByIdAsync(item.ProductId);
                if (product == null) continue;

                var productDto = allProductDtos.FirstOrDefault(x => x.Id == product.Id);
                var isMeasurable = productDto?.IsMeasurable ?? false;

                decimal? unitPrice = null;
                if (!isMeasurable)
                {
                    var priceResult = await _customPriceCalculationService.GetFinalPriceAsync(
                        product, customer, store, null, 0, true, item.Quantity, null, null);
                    unitPrice = priceResult.finalPrice;
                }

                result.Add(new
                {
                    id = item.Id,
                    productId = item.ProductId,
                    name = product.Name,
                    quantity = item.Quantity,
                    unitPrice,
                    isMeasurable
                });
            }

            return result;
        }

        #endregion

        #region Inner models

        private class ParsedProduct
        {
            [System.Text.Json.Serialization.JsonPropertyName("product")]
            public string Product { get; set; }

            [System.Text.Json.Serialization.JsonPropertyName("quantity")]
            public int Quantity { get; set; }
        }

        #endregion
    }
}
