using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Meezi.API.Models.Menu;
using Meezi.API.Services;
using Meezi.Core.Enums;
using Meezi.Core.Interfaces;
using Meezi.Shared;

namespace Meezi.API.Controllers;

[Route("api/cafes/{cafeId}/menu")]
public class MenuController : CafeApiControllerBase
{
    private readonly IMenuService _menuService;
    private readonly IMenuAi3dGenerationService _menuAi3d;
    private readonly IValidator<CreateMenuCategoryRequest> _createCategoryValidator;
    private readonly IValidator<CreateMenuItemRequest> _createItemValidator;

    public MenuController(
        IMenuService menuService,
        IMenuAi3dGenerationService menuAi3d,
        IValidator<CreateMenuCategoryRequest> createCategoryValidator,
        IValidator<CreateMenuItemRequest> createItemValidator)
    {
        _menuService = menuService;
        _menuAi3d = menuAi3d;
        _createCategoryValidator = createCategoryValidator;
        _createItemValidator = createItemValidator;
    }

    [HttpGet("categories")]
    public async Task<IActionResult> GetCategories(string cafeId, ITenantContext tenant, CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var data = await _menuService.GetCategoriesAsync(cafeId, cancellationToken);
        return Ok(new ApiResponse<IReadOnlyList<MenuCategoryDto>>(true, data));
    }

    [HttpPost("categories")]
    public async Task<IActionResult> CreateCategory(
        string cafeId,
        [FromBody] CreateMenuCategoryRequest request,
        ITenantContext tenant,
        CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var validation = await _createCategoryValidator.ValidateAsync(request, cancellationToken);
        if (!validation.IsValid) return BadRequest(ValidationError(validation));

        var data = await _menuService.CreateCategoryAsync(cafeId, request, cancellationToken);
        return Ok(new ApiResponse<MenuCategoryDto>(true, data));
    }

    [HttpPatch("categories/{id}")]
    public async Task<IActionResult> UpdateCategory(
        string cafeId,
        string id,
        [FromBody] UpdateMenuCategoryRequest request,
        ITenantContext tenant,
        CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var data = await _menuService.UpdateCategoryAsync(cafeId, id, request, cancellationToken);
        if (data is null) return NotFoundError();
        return Ok(new ApiResponse<MenuCategoryDto>(true, data));
    }

    [HttpDelete("categories/{id}")]
    public async Task<IActionResult> DeleteCategory(string cafeId, string id, ITenantContext tenant, CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var deleted = await _menuService.DeleteCategoryAsync(cafeId, id, cancellationToken);
        if (!deleted) return NotFoundError();
        return Ok(new ApiResponse<object>(true, new { id }));
    }

    [HttpGet("items")]
    public async Task<IActionResult> GetItems(
        string cafeId,
        [FromQuery] string? categoryId,
        ITenantContext tenant,
        CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var data = await _menuService.GetItemsAsync(cafeId, categoryId, cancellationToken);
        return Ok(new ApiResponse<IReadOnlyList<MenuItemDto>>(true, data));
    }

    [HttpPost("items")]
    public async Task<IActionResult> CreateItem(
        string cafeId,
        [FromBody] CreateMenuItemRequest request,
        ITenantContext tenant,
        CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var validation = await _createItemValidator.ValidateAsync(request, cancellationToken);
        if (!validation.IsValid) return BadRequest(ValidationError(validation));

        var data = await _menuService.CreateItemAsync(cafeId, request, cancellationToken);
        if (data is null) return NotFoundError("Category not found.");
        return Ok(new ApiResponse<MenuItemDto>(true, data));
    }

    [HttpPatch("items/{id}")]
    public async Task<IActionResult> UpdateItem(
        string cafeId,
        string id,
        [FromBody] UpdateMenuItemRequest request,
        ITenantContext tenant,
        CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var data = await _menuService.UpdateItemAsync(cafeId, id, request, cancellationToken);
        if (data is null) return NotFoundError();
        return Ok(new ApiResponse<MenuItemDto>(true, data));
    }

    [HttpPatch("items/{id}/availability")]
    public async Task<IActionResult> SetAvailability(
        string cafeId,
        string id,
        [FromBody] UpdateMenuItemAvailabilityRequest request,
        ITenantContext tenant,
        CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var data = await _menuService.SetAvailabilityAsync(cafeId, id, request.IsAvailable, cancellationToken);
        if (data is null) return NotFoundError();
        return Ok(new ApiResponse<MenuItemDto>(true, data));
    }

    [HttpGet("ai-3d/usage")]
    public async Task<IActionResult> GetAi3dUsage(string cafeId, ITenantContext tenant, CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var tier = tenant.PlanTier ?? PlanTier.Free;
        var data = await _menuAi3d.GetUsageAsync(cafeId, tier, cancellationToken);
        return Ok(new ApiResponse<MenuAi3dUsageDto>(true, data));
    }

    [HttpPost("items/{id}/ai-3d")]
    public async Task<IActionResult> GenerateAi3d(
        string cafeId,
        string id,
        ITenantContext tenant,
        CancellationToken cancellationToken)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var tier = tenant.PlanTier ?? PlanTier.Free;
        var (data, code, message) = await _menuAi3d.GenerateFromItemImageAsync(cafeId, id, tier, cancellationToken);
        if (code is not null)
        {
            return code switch
            {
                "NOT_FOUND" => NotFound(new ApiResponse<object>(false, null, new ApiError(code, message ?? ""))),
                "PLAN_FEATURE_DISABLED" => StatusCode(
                    StatusCodes.Status403Forbidden,
                    new ApiResponse<object>(false, null, new ApiError(code, message ?? ""))),
                "PLAN_LIMIT_REACHED" => StatusCode(
                    StatusCodes.Status403Forbidden,
                    new ApiResponse<object>(false, null, new ApiError(code, message ?? ""))),
                _ => BadRequest(new ApiResponse<object>(false, null, new ApiError(code, message ?? "")))
            };
        }

        return Ok(new ApiResponse<MenuAi3dGenerateResultDto>(true, data));
    }
}
