using FruitBank.Common.Server;
using LinqToDB;
using Microsoft.AspNetCore.Mvc;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Security;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc.Filters;

namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers;

[AuthorizeAdmin]
[Area(AreaNames.ADMIN)]
[AutoValidateAntiforgeryToken]
public class PreOrderAvailabilityController : BasePluginController
{
    private readonly IPermissionService _permissionService;
    private readonly FruitBankDbContext _dbContext;
    private readonly FruitBankAttributeService _fruitBankAttributeService;
    private readonly IStoreContext _storeContext;

    public PreOrderAvailabilityController(
        IPermissionService permissionService,
        FruitBankDbContext dbContext,
        FruitBankAttributeService fruitBankAttributeService,
        IStoreContext storeContext)
    {
        _permissionService = permissionService;
        _dbContext = dbContext;
        _fruitBankAttributeService = fruitBankAttributeService;
        _storeContext = storeContext;
    }

    // ── INDEX ─────────────────────────────────────────────────────────────────

    [HttpGet]
    [Route("Admin/PreOrderAvailability")]
    public async Task<IActionResult> Index()
    {
        if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
            return AccessDeniedView();

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

    // ── ALL PRODUCTS — DataTables server-side ─────────────────────────────────

    [HttpPost]
    [Route("Admin/PreOrderAvailability/ProductList")]
    public async Task<IActionResult> ProductList()
    {
        if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
            return Forbid();

        _ = int.TryParse(Request.Form["draw"].FirstOrDefault(),   out int draw);   draw   = Math.Max(draw, 1);
        _ = int.TryParse(Request.Form["start"].FirstOrDefault(),  out int start);  start  = Math.Max(start, 0);
        _ = int.TryParse(Request.Form["length"].FirstOrDefault(), out int length); length = length < 1 ? 25 : Math.Min(length, 500);

        var globalSearch = Request.Form["search[value]"].FirstOrDefault()?.Trim() ?? "";

        var storeId = (await _storeContext.GetCurrentStoreAsync()).Id;

        // 1. All published products
        var products = await _dbContext.Products.Table
            .Where(p => !p.Deleted && p.Published)
            .OrderBy(p => p.Name)
            .Select(p => new { p.Id, p.Name, p.Sku })
            .ToListAsync();

        // 2. All preorder window generic attributes — two queries, no N+1
        var gaStart = await _dbContext.GenericAttributes.Table
            .Where(ga => ga.KeyGroup == nameof(Product)
                      && ga.Key == FruitBankConst.PreOrderWindowStart
                      && ga.StoreId == storeId)
            .ToListAsync();

        var gaEnd = await _dbContext.GenericAttributes.Table
            .Where(ga => ga.KeyGroup == nameof(Product)
                      && ga.Key == FruitBankConst.PreOrderWindowEnd
                      && ga.StoreId == storeId)
            .ToListAsync();

        var startByProduct = gaStart.ToDictionary(g => g.EntityId, g => g.Value);
        var endByProduct   = gaEnd.ToDictionary(g => g.EntityId, g => g.Value);

        var today = DateTime.UtcNow.Date;

        // 3. Build rows
        var rows = products.Select(p =>
        {
            DateTime.TryParse(startByProduct.GetValueOrDefault(p.Id), out var ws);
            DateTime.TryParse(endByProduct.GetValueOrDefault(p.Id),   out var we);

            var hasStart = startByProduct.ContainsKey(p.Id);
            var hasEnd   = endByProduct.ContainsKey(p.Id);

            return new PreOrderAvailabilityRow
            {
                ProductId        = p.Id,
                ProductName      = p.Name,
                Sku              = p.Sku,
                WindowStart      = hasStart ? ws.ToString("yyyy-MM-dd") : null,
                WindowEnd        = hasEnd   ? we.ToString("yyyy-MM-dd") : null,
                IsAvailableToday = hasStart && hasEnd && ws.Date <= today && today <= we.Date
            };
        }).ToList();

        int recordsTotal = rows.Count;

        // 4. Global search
        if (!string.IsNullOrWhiteSpace(globalSearch))
        {
            rows = rows.Where(r =>
                r.ProductName.Contains(globalSearch, StringComparison.OrdinalIgnoreCase) ||
                (r.Sku?.Contains(globalSearch, StringComparison.OrdinalIgnoreCase) ?? false)
            ).ToList();
        }

        int recordsFiltered = rows.Count;

        // 5. Paginate
        var page = rows.Skip(start).Take(length).ToList();

        return Json(new { draw, recordsTotal, recordsFiltered, data = page });
    }

    // ── AVAILABLE TODAY — DataTables server-side ──────────────────────────────

    [HttpPost]
    [Route("Admin/PreOrderAvailability/AvailableTodayList")]
    public async Task<IActionResult> AvailableTodayList()
    {
        if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
            return Forbid();

        _ = int.TryParse(Request.Form["draw"].FirstOrDefault(),   out int draw);   draw   = Math.Max(draw, 1);
        _ = int.TryParse(Request.Form["start"].FirstOrDefault(),  out int start);  start  = Math.Max(start, 0);
        _ = int.TryParse(Request.Form["length"].FirstOrDefault(), out int length); length = length < 1 ? 25 : Math.Min(length, 500);

        var storeId = (await _storeContext.GetCurrentStoreAsync()).Id;
        var today   = DateTime.UtcNow.Date;

        // Reuse same build logic — filter to available today only
        var products = await _dbContext.Products.Table
            .Where(p => !p.Deleted && p.Published)
            .OrderBy(p => p.Name)
            .Select(p => new { p.Id, p.Name, p.Sku })
            .ToListAsync();

        var gaStart = await _dbContext.GenericAttributes.Table
            .Where(ga => ga.KeyGroup == nameof(Product)
                      && ga.Key == FruitBankConst.PreOrderWindowStart
                      && ga.StoreId == storeId)
            .ToListAsync();

        var gaEnd = await _dbContext.GenericAttributes.Table
            .Where(ga => ga.KeyGroup == nameof(Product)
                      && ga.Key == FruitBankConst.PreOrderWindowEnd
                      && ga.StoreId == storeId)
            .ToListAsync();

        var startByProduct = gaStart.ToDictionary(g => g.EntityId, g => g.Value);
        var endByProduct   = gaEnd.ToDictionary(g => g.EntityId, g => g.Value);

        var rows = products
            .Where(p =>
            {
                if (!startByProduct.TryGetValue(p.Id, out var sRaw)) return false;
                if (!endByProduct.TryGetValue(p.Id, out var eRaw))   return false;
                if (!DateTime.TryParse(sRaw, out var ws)) return false;
                if (!DateTime.TryParse(eRaw, out var we)) return false;
                return ws.Date <= today && today <= we.Date;
            })
            .Select(p =>
            {
                DateTime.TryParse(startByProduct[p.Id], out var ws);
                DateTime.TryParse(endByProduct[p.Id],   out var we);
                return new PreOrderAvailabilityRow
                {
                    ProductId        = p.Id,
                    ProductName      = p.Name,
                    Sku              = p.Sku,
                    WindowStart      = ws.ToString("yyyy-MM-dd"),
                    WindowEnd        = we.ToString("yyyy-MM-dd"),
                    IsAvailableToday = true
                };
            })
            .ToList();

        int recordsTotal    = rows.Count;
        int recordsFiltered = rows.Count;

        var page = rows.Skip(start).Take(length).ToList();
        return Json(new { draw, recordsTotal, recordsFiltered, data = page });
    }

    // ── SAVE WINDOW DATES for a product ───────────────────────────────────────

    [HttpPost]
    [Route("Admin/PreOrderAvailability/SaveWindow")]
    public async Task<IActionResult> SaveWindow(int productId, string? windowStart, string? windowEnd)
    {
        if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
            return Json(new { success = false, error = "Access denied" });

        try
        {
            var storeId = (await _storeContext.GetCurrentStoreAsync()).Id;

            // WindowStart
            if (string.IsNullOrWhiteSpace(windowStart))
            {
                await _fruitBankAttributeService
                    .DeleteGenericAttributeAsync<Product>(productId, FruitBankConst.PreOrderWindowStart, storeId);
            }
            else if (DateTime.TryParse(windowStart, out var ws))
            {
                await _fruitBankAttributeService
                    .InsertOrUpdateGenericAttributeAsync<Product, DateTime>(
                        productId, FruitBankConst.PreOrderWindowStart, ws.Date, storeId);
            }
            else return Json(new { success = false, error = $"Invalid start date: {windowStart}" });

            // WindowEnd
            if (string.IsNullOrWhiteSpace(windowEnd))
            {
                await _fruitBankAttributeService
                    .DeleteGenericAttributeAsync<Product>(productId, FruitBankConst.PreOrderWindowEnd, storeId);
            }
            else if (DateTime.TryParse(windowEnd, out var we))
            {
                await _fruitBankAttributeService
                    .InsertOrUpdateGenericAttributeAsync<Product, DateTime>(
                        productId, FruitBankConst.PreOrderWindowEnd, we.Date, storeId);
            }
            else return Json(new { success = false, error = $"Invalid end date: {windowEnd}" });

            // Return the new availability state
            var today = DateTime.UtcNow.Date;
            DateTime.TryParse(windowStart, out var startParsed);
            DateTime.TryParse(windowEnd,   out var endParsed);
            bool isAvailableToday = !string.IsNullOrWhiteSpace(windowStart)
                                 && !string.IsNullOrWhiteSpace(windowEnd)
                                 && startParsed.Date <= today
                                 && today <= endParsed.Date;

            return Json(new { success = true, isAvailableToday });
        }
        catch (Exception ex)
        {
            return Json(new { success = false, error = ex.Message });
        }
    }
}
