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

namespace Meezi.API.Services;

public interface IBranchMenuService
{
    Task<IReadOnlyList<BranchMenuItemDto>?> GetBranchMenuAsync(
        string cafeId,
        string branchId,
        bool includeUnavailable,
        CancellationToken cancellationToken = default);

    Task<(bool Success, BranchMenuOverrideDto? Data, string? ErrorCode, string? Message)> UpsertOverrideAsync(
        string cafeId,
        string branchId,
        string menuItemId,
        UpsertBranchMenuOverrideRequest request,
        PlanTier planTier,
        EmployeeRole? role,
        string? userId,
        CancellationToken cancellationToken = default);

    Task<bool> DeleteOverrideAsync(
        string cafeId,
        string branchId,
        string menuItemId,
        CancellationToken cancellationToken = default);
}

public class BranchMenuService : IBranchMenuService
{
    private readonly AppDbContext _db;

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

    public async Task<IReadOnlyList<BranchMenuItemDto>?> GetBranchMenuAsync(
        string cafeId,
        string branchId,
        bool includeUnavailable,
        CancellationToken cancellationToken = default)
    {
        var branchOk = await _db.Branches.AnyAsync(
            b => b.Id == branchId && b.CafeId == cafeId,
            cancellationToken);
        if (!branchOk) return null;

        var items = await _db.MenuItems
            .Include(i => i.Category)
            .Where(i => i.CafeId == cafeId && i.Category.IsActive)
            .OrderBy(i => i.Category.SortOrder)
            .ThenBy(i => i.Name)
            .ToListAsync(cancellationToken);

        var overrides = await _db.BranchMenuItemOverrides
            .Where(o => o.CafeId == cafeId && o.BranchId == branchId)
            .ToDictionaryAsync(o => o.MenuItemId, cancellationToken);

        var result = new List<BranchMenuItemDto>();

        foreach (var item in items)
        {
            overrides.TryGetValue(item.Id, out var ov);
            var branchAvailable = ov?.IsAvailable ?? true;
            var catalogAvailable = item.IsAvailable;

            if (!includeUnavailable)
            {
                if (!catalogAvailable || !branchAvailable) continue;
            }
            else if (!catalogAvailable && ov is null)
            {
                continue;
            }

            var masterPrice = item.Price;
            var effectivePrice = ov?.PriceOverride ?? masterPrice;
            var hasPriceOverride = ov?.PriceOverride is not null;

            result.Add(new BranchMenuItemDto(
                item.Id,
                item.CategoryId,
                item.Name,
                item.NameAr,
                item.NameEn,
                item.Description,
                masterPrice,
                effectivePrice,
                item.DiscountPercent,
                item.ImageUrl,
                item.VideoUrl,
                item.Model3dUrl,
                branchAvailable && catalogAvailable,
                ov is not null,
                hasPriceOverride));
        }

        return result;
    }

    public async Task<(bool Success, BranchMenuOverrideDto? Data, string? ErrorCode, string? Message)> UpsertOverrideAsync(
        string cafeId,
        string branchId,
        string menuItemId,
        UpsertBranchMenuOverrideRequest request,
        PlanTier planTier,
        EmployeeRole? role,
        string? userId,
        CancellationToken cancellationToken = default)
    {
        if (request.PriceOverride is not null && !CanSetPriceOverride(planTier, role))
            return (false, null, "PLAN_LIMIT_REACHED",
                "قیمت‌گذاری شعبه‌ای نیاز به پلن Pro دارد");

        var branchOk = await _db.Branches.AnyAsync(
            b => b.Id == branchId && b.CafeId == cafeId,
            cancellationToken);
        if (!branchOk) return (false, null, "BRANCH_NOT_FOUND", "Branch not found.");

        var item = await _db.MenuItems.FirstOrDefaultAsync(
            i => i.Id == menuItemId && i.CafeId == cafeId,
            cancellationToken);
        if (item is null) return (false, null, "NOT_FOUND", "Menu item not found.");

        if (request.PriceOverride is < 0)
            return (false, null, "VALIDATION_ERROR", "Price override must be non-negative.");

        var existing = await _db.BranchMenuItemOverrides.FirstOrDefaultAsync(
            o => o.BranchId == branchId && o.MenuItemId == menuItemId,
            cancellationToken);

        if (existing is null)
        {
            existing = new BranchMenuItemOverride
            {
                CafeId = cafeId,
                BranchId = branchId,
                MenuItemId = menuItemId,
            };
            _db.BranchMenuItemOverrides.Add(existing);
        }

        existing.IsAvailable = request.IsAvailable;
        existing.PriceOverride = request.PriceOverride;
        existing.UpdatedAt = DateTime.UtcNow;
        existing.UpdatedByUserId = userId;

        await _db.SaveChangesAsync(cancellationToken);

        return (true, new BranchMenuOverrideDto(
            menuItemId,
            branchId,
            existing.IsAvailable,
            existing.PriceOverride,
            existing.UpdatedAt), null, null);
    }

    public async Task<bool> DeleteOverrideAsync(
        string cafeId,
        string branchId,
        string menuItemId,
        CancellationToken cancellationToken = default)
    {
        var row = await _db.BranchMenuItemOverrides.FirstOrDefaultAsync(
            o => o.CafeId == cafeId && o.BranchId == branchId && o.MenuItemId == menuItemId,
            cancellationToken);
        if (row is null) return false;

        _db.BranchMenuItemOverrides.Remove(row);
        await _db.SaveChangesAsync(cancellationToken);
        return true;
    }

    internal static bool CanSetPriceOverride(PlanTier planTier, EmployeeRole? role) =>
        role is EmployeeRole.Owner || planTier is not PlanTier.Free;

    internal static bool CanManageOverrides(EmployeeRole? role) =>
        role is EmployeeRole.Owner or EmployeeRole.Manager;
}
