using Meezi.API.Models.Menu;
using Meezi.Core.Constants;
using Meezi.Core.Entities;
using Meezi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace Meezi.API.Services;

public interface IMenuService
{
    Task<IReadOnlyList<MenuCategoryDto>> GetCategoriesAsync(string cafeId, CancellationToken cancellationToken = default);
    Task<MenuCategoryDto?> CreateCategoryAsync(string cafeId, CreateMenuCategoryRequest request, CancellationToken cancellationToken = default);
    Task<MenuCategoryDto?> UpdateCategoryAsync(string cafeId, string id, UpdateMenuCategoryRequest request, CancellationToken cancellationToken = default);
    Task<bool> DeleteCategoryAsync(string cafeId, string id, CancellationToken cancellationToken = default);
    Task<IReadOnlyList<MenuItemDto>> GetItemsAsync(string cafeId, string? categoryId, CancellationToken cancellationToken = default);
    Task<MenuItemDto?> CreateItemAsync(string cafeId, CreateMenuItemRequest request, CancellationToken cancellationToken = default);
    Task<MenuItemDto?> UpdateItemAsync(string cafeId, string id, UpdateMenuItemRequest request, CancellationToken cancellationToken = default);
    Task<MenuItemDto?> SetAvailabilityAsync(string cafeId, string id, bool isAvailable, CancellationToken cancellationToken = default);
}

public class MenuService : IMenuService
{
    private readonly AppDbContext _db;

    public MenuService(AppDbContext db)
    {
        _db = db;
    }

    public async Task<IReadOnlyList<MenuCategoryDto>> GetCategoriesAsync(string cafeId, CancellationToken cancellationToken = default)
    {
        return await _db.MenuCategories
            .Where(c => c.CafeId == cafeId)
            .OrderBy(c => c.SortOrder)
            .Select(c => ToCategoryDto(c))
            .ToListAsync(cancellationToken);
    }

    public async Task<MenuCategoryDto?> CreateCategoryAsync(string cafeId, CreateMenuCategoryRequest request, CancellationToken cancellationToken = default)
    {
        var entity = new MenuCategory
        {
            CafeId = cafeId,
            Name = request.Name,
            NameAr = request.NameAr,
            NameEn = request.NameEn,
            SortOrder = request.SortOrder,
            TaxId = request.TaxId,
            DiscountPercent = request.DiscountPercent,
            Icon = NormalizeOptionalText(request.Icon),
            IconPresetId = NormalizeIconPreset(request.IconPresetId),
            IconStyle = NormalizeIconStyle(request.IconStyle)
                ?? (NormalizeIconPreset(request.IconPresetId) is not null
                    ? CategoryIconPresets.IconStyle.Flat
                    : null),
            ImageUrl = NormalizeOptionalText(request.ImageUrl),
            IsActive = request.IsActive,
            KitchenStationId = string.IsNullOrWhiteSpace(request.KitchenStationId)
                ? null
                : request.KitchenStationId
        };

        _db.MenuCategories.Add(entity);
        await _db.SaveChangesAsync(cancellationToken);
        return ToCategoryDto(entity);
    }

    public async Task<MenuCategoryDto?> UpdateCategoryAsync(string cafeId, string id, UpdateMenuCategoryRequest request, CancellationToken cancellationToken = default)
    {
        var entity = await _db.MenuCategories.FirstOrDefaultAsync(c => c.Id == id && c.CafeId == cafeId, cancellationToken);
        if (entity is null) return null;

        if (request.Name is not null) entity.Name = request.Name;
        if (request.NameAr is not null) entity.NameAr = request.NameAr;
        if (request.NameEn is not null) entity.NameEn = request.NameEn;
        if (request.SortOrder.HasValue) entity.SortOrder = request.SortOrder.Value;
        if (request.TaxId is not null) entity.TaxId = request.TaxId;
        if (request.DiscountPercent.HasValue) entity.DiscountPercent = request.DiscountPercent.Value;
        if (request.IsActive.HasValue) entity.IsActive = request.IsActive.Value;
        if (request.Icon is not null)
            entity.Icon = NormalizeOptionalText(request.Icon);
        if (request.IconPresetId is not null)
            entity.IconPresetId = NormalizeIconPreset(request.IconPresetId);
        if (request.IconStyle is not null)
            entity.IconStyle = NormalizeIconStyle(request.IconStyle);
        if (request.ImageUrl is not null)
            entity.ImageUrl = NormalizeOptionalText(request.ImageUrl);
        if (request.KitchenStationId is not null)
            entity.KitchenStationId = string.IsNullOrWhiteSpace(request.KitchenStationId)
                ? null
                : request.KitchenStationId;

        await _db.SaveChangesAsync(cancellationToken);
        return ToCategoryDto(entity);
    }

    public async Task<bool> DeleteCategoryAsync(string cafeId, string id, CancellationToken cancellationToken = default)
    {
        var entity = await _db.MenuCategories.FirstOrDefaultAsync(c => c.Id == id && c.CafeId == cafeId, cancellationToken);
        if (entity is null) return false;

        entity.DeletedAt = DateTime.UtcNow;
        await _db.SaveChangesAsync(cancellationToken);
        return true;
    }

    public async Task<IReadOnlyList<MenuItemDto>> GetItemsAsync(string cafeId, string? categoryId, CancellationToken cancellationToken = default)
    {
        var query = _db.MenuItems.Where(i => i.CafeId == cafeId);
        if (!string.IsNullOrEmpty(categoryId))
            query = query.Where(i => i.CategoryId == categoryId);

        var items = await query.Include(i => i.Category).OrderBy(i => i.Name).ToListAsync(cancellationToken);
        return items.Select(ToItemDto).ToList();
    }

    public async Task<MenuItemDto?> CreateItemAsync(string cafeId, CreateMenuItemRequest request, CancellationToken cancellationToken = default)
    {
        var category = await _db.MenuCategories
            .FirstOrDefaultAsync(c => c.Id == request.CategoryId && c.CafeId == cafeId, cancellationToken);
        if (category is null) return null;

        var imageUrl = string.IsNullOrWhiteSpace(request.ImageUrl)
            ? MenuItemImageDefaults.GetDefaultImageUrl(
                MenuItemImageDefaults.InferKind(request.CategoryId, category.Name))
            : request.ImageUrl;

        var entity = new MenuItem
        {
            CafeId = cafeId,
            CategoryId = request.CategoryId,
            Name = request.Name,
            NameAr = request.NameAr,
            NameEn = request.NameEn,
            Description = request.Description,
            Price = request.Price,
            DiscountPercent = request.DiscountPercent,
            ImageUrl = imageUrl,
            VideoUrl = request.VideoUrl,
            Model3dUrl = NormalizeOptionalText(request.Model3dUrl),
            IsAvailable = request.IsAvailable
        };

        _db.MenuItems.Add(entity);
        await _db.SaveChangesAsync(cancellationToken);
        return ToItemDto(entity);
    }

    public async Task<MenuItemDto?> UpdateItemAsync(string cafeId, string id, UpdateMenuItemRequest request, CancellationToken cancellationToken = default)
    {
        var entity = await _db.MenuItems
            .Include(i => i.Category)
            .FirstOrDefaultAsync(i => i.Id == id && i.CafeId == cafeId, cancellationToken);
        if (entity is null) return null;

        if (request.CategoryId is not null)
        {
            var categoryExists = await _db.MenuCategories.AnyAsync(c => c.Id == request.CategoryId && c.CafeId == cafeId, cancellationToken);
            if (!categoryExists) return null;
            entity.CategoryId = request.CategoryId;
        }

        if (request.Name is not null) entity.Name = request.Name;
        if (request.NameAr is not null) entity.NameAr = request.NameAr;
        if (request.NameEn is not null) entity.NameEn = request.NameEn;
        if (request.Description is not null) entity.Description = request.Description;
        if (request.Price.HasValue) entity.Price = request.Price.Value;
        if (request.DiscountPercent.HasValue) entity.DiscountPercent = request.DiscountPercent.Value;
        if (request.ImageUrl is not null)
        {
            entity.ImageUrl = string.IsNullOrWhiteSpace(request.ImageUrl)
                ? MenuItemImageDefaults.ResolveImageUrl(entity.Id, entity.CategoryId, entity.Category?.Name)
                : request.ImageUrl;
        }
        if (request.VideoUrl is not null)
            entity.VideoUrl = string.IsNullOrWhiteSpace(request.VideoUrl) ? null : request.VideoUrl;
        if (request.Model3dUrl is not null)
            entity.Model3dUrl = string.IsNullOrWhiteSpace(request.Model3dUrl) ? null : request.Model3dUrl.Trim();
        if (request.IsAvailable.HasValue) entity.IsAvailable = request.IsAvailable.Value;

        await _db.SaveChangesAsync(cancellationToken);
        return ToItemDto(entity);
    }

    public async Task<MenuItemDto?> SetAvailabilityAsync(string cafeId, string id, bool isAvailable, CancellationToken cancellationToken = default)
    {
        var entity = await _db.MenuItems.FirstOrDefaultAsync(i => i.Id == id && i.CafeId == cafeId, cancellationToken);
        if (entity is null) return null;

        entity.IsAvailable = isAvailable;
        await _db.SaveChangesAsync(cancellationToken);
        return ToItemDto(entity);
    }

    private static string? NormalizeOptionalText(string? value) =>
        string.IsNullOrWhiteSpace(value) ? null : value.Trim();

    private static string? NormalizeIconPreset(string? value)
    {
        var normalized = CategoryIconPresets.NormalizePreset(value);
        if (normalized is null) return null;
        return CategoryIconPresets.IsValidPreset(normalized) ? normalized : null;
    }

    private static string? NormalizeIconStyle(string? value)
    {
        var normalized = CategoryIconPresets.NormalizeStyle(value);
        if (normalized is null) return null;
        return CategoryIconPresets.IsValidStyle(normalized) ? normalized : null;
    }

    private static MenuCategoryDto ToCategoryDto(MenuCategory c) => new(
        c.Id, c.Name, c.NameAr, c.NameEn, c.SortOrder, c.TaxId, c.DiscountPercent,
        c.Icon, c.IconPresetId, c.IconStyle, c.ImageUrl, c.IsActive, c.KitchenStationId);

    private static MenuItemDto ToItemDto(MenuItem i) => new(
        i.Id,
        i.CategoryId,
        i.Name,
        i.NameAr,
        i.NameEn,
        i.Description,
        i.Price,
        i.DiscountPercent,
        MenuItemImageDefaults.ResolveDisplayImageUrl(i),
        i.VideoUrl,
        i.Model3dUrl,
        i.IsAvailable);
}
