using System.Globalization;
using CartWise.Application.Interfaces;
using CartWise.Application.Results;
using CartWise.Infrastructure.Identity;
using CartWise.Web.ViewModels.ShoppingList;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace CartWise.Web.Controllers;

[Authorize]
[Route("list")]
public class ShoppingListController : Controller
{
    private readonly IShoppingListService _shoppingListService;
    private readonly IHouseholdService _householdService;
    private readonly UserManager<ApplicationUser> _userManager;

    public ShoppingListController(
        IShoppingListService shoppingListService,
        IHouseholdService householdService,
        UserManager<ApplicationUser> userManager)
    {
        _shoppingListService = shoppingListService;
        _householdService = householdService;
        _userManager = userManager;
    }

    [HttpGet("")]
    public async Task<IActionResult> Index()
    {
        var userId = _userManager.GetUserId(User)!;
        var household = await _householdService.GetCurrentHouseholdAsync(userId);
        if (household is null)
        {
            return RedirectToAction("Create", "Household");
        }

        var list = await GetOrCreateActiveListAsync(household.HouseholdId, userId);
        return View(ToViewModel(list));
    }

    [HttpPost("items")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> AddItem([Bind(Prefix = "NewItem")] AddShoppingListItemViewModel model)
    {
        var userId = _userManager.GetUserId(User)!;
        var household = await _householdService.GetCurrentHouseholdAsync(userId);
        if (household is null)
        {
            return RedirectToAction("Create", "Household");
        }

        var list = await GetOrCreateActiveListAsync(household.HouseholdId, userId);

        if (!ModelState.IsValid)
        {
            var viewModel = ToViewModel(list);
            viewModel.NewItem = model;
            return View(nameof(Index), viewModel);
        }

        var result = await _shoppingListService.AddItemAsync(list.ShoppingListId, model.DisplayName, model.Quantity, model.Unit, userId);
        if (!result.IsSuccess)
        {
            ModelState.AddModelError(string.Empty, result.Error!);
            var viewModel = ToViewModel(list);
            viewModel.NewItem = model;
            return View(nameof(Index), viewModel);
        }

        return RedirectToAction(nameof(Index));
    }

    [HttpPost("items/{id:guid}/toggle")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> TogglePurchased(Guid id, Guid rowVersion)
    {
        var household = await CurrentHouseholdOrNullAsync();
        if (household is null)
        {
            return RedirectToAction("Create", "Household");
        }

        var result = await _shoppingListService.TogglePurchasedAsync(household.HouseholdId, id, rowVersion);
        return RespondToItemMutation(result);
    }

    [HttpPost("items/{id:guid}/skip")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ToggleSkipped(Guid id, Guid rowVersion)
    {
        var household = await CurrentHouseholdOrNullAsync();
        if (household is null)
        {
            return RedirectToAction("Create", "Household");
        }

        var result = await _shoppingListService.ToggleSkippedAsync(household.HouseholdId, id, rowVersion);
        return RespondToItemMutation(result);
    }

    [HttpPost("items/{id:guid}/delete")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> DeleteItem(Guid id, Guid rowVersion)
    {
        var household = await CurrentHouseholdOrNullAsync();
        if (household is null)
        {
            return RedirectToAction("Create", "Household");
        }

        var result = await _shoppingListService.DeleteItemAsync(household.HouseholdId, id, rowVersion);

        if (IsAjaxRequest())
        {
            return result.IsSuccess ? NoContent() : Problem(result.Error, statusCode: StatusCodes.Status409Conflict);
        }

        if (!result.IsSuccess)
        {
            TempData["ListError"] = result.Error;
        }

        return RedirectToAction(nameof(Index));
    }

    private async Task<Domain.Entities.Household?> CurrentHouseholdOrNullAsync()
    {
        var userId = _userManager.GetUserId(User)!;
        return await _householdService.GetCurrentHouseholdAsync(userId);
    }

    private IActionResult RespondToItemMutation(Result<Domain.Entities.ShoppingListItem> result)
    {
        if (IsAjaxRequest())
        {
            return result.IsSuccess
                ? PartialView("_ListItem", ToItemViewModel(result.Value!))
                : Problem(result.Error, statusCode: StatusCodes.Status409Conflict);
        }

        if (!result.IsSuccess)
        {
            TempData["ListError"] = result.Error;
        }

        return RedirectToAction(nameof(Index));
    }

    private bool IsAjaxRequest() => Request.Headers["X-Requested-With"] == "XMLHttpRequest";

    private async Task<Domain.Entities.ShoppingList> GetOrCreateActiveListAsync(Guid householdId, string userId)
    {
        var list = await _shoppingListService.GetActiveListAsync(householdId);
        if (list is not null)
        {
            return list;
        }

        var createResult = await _shoppingListService.CreateListAsync(householdId, "Groceries", userId);
        return createResult.Value ?? await _shoppingListService.GetActiveListAsync(householdId)
            ?? throw new InvalidOperationException("Unable to resolve the household's active shopping list.");
    }

    private static ShoppingListViewModel ToViewModel(Domain.Entities.ShoppingList list) => new()
    {
        ShoppingListId = list.ShoppingListId,
        Name = list.Name,
        Items = list.Items
            .OrderBy(i => i.SortOrder)
            .Select(ToItemViewModel)
            .ToList()
    };

    private static ShoppingListItemViewModel ToItemViewModel(Domain.Entities.ShoppingListItem item) => new()
    {
        Id = item.ShoppingListItemId,
        DisplayName = item.DisplayName,
        QuantityDisplay = FormatQuantity(item.Quantity, item.Unit),
        Status = item.Status,
        ConcurrencyToken = item.RowVersion
    };

    private static string? FormatQuantity(decimal? quantity, string? unit)
    {
        if (quantity is null)
        {
            return unit;
        }

        var quantityText = quantity.Value.ToString("0.###", CultureInfo.InvariantCulture);
        return unit is null ? quantityText : $"{quantityText} {unit}";
    }
}
