using Meezi.Infrastructure.Services.Platform;
using Meezi.Core.Enums;
using Meezi.Core.Interfaces;
using Meezi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;

namespace Meezi.API.Services;

public interface IPlanLimitChecker
{
    Task<(bool Allowed, string? ErrorCode, string? Message)> CheckAsync(
        HttpContext context,
        ITenantContext tenant,
        CancellationToken cancellationToken = default);
}

public class PlanLimitChecker : IPlanLimitChecker
{
    private readonly AppDbContext _db;
    private readonly IConnectionMultiplexer _redis;
    private readonly IPlatformCatalogService _platformCatalog;

    public PlanLimitChecker(
        AppDbContext db,
        IConnectionMultiplexer redis,
        IPlatformCatalogService platformCatalog)
    {
        _db = db;
        _redis = redis;
        _platformCatalog = platformCatalog;
    }

    public async Task<(bool Allowed, string? ErrorCode, string? Message)> CheckAsync(
        HttpContext context,
        ITenantContext tenant,
        CancellationToken cancellationToken = default)
    {
        if (tenant.IsSystemAdmin || !tenant.IsAuthenticated || tenant.PlanTier is null || string.IsNullOrEmpty(tenant.CafeId))
            return (true, null, null);

        var method = context.Request.Method;
        var path = context.Request.Path.Value ?? string.Empty;

        if (method != HttpMethods.Post)
            return (true, null, null);

        var cafeId = tenant.CafeId;
        var tier = tenant.PlanTier.Value;

        var ordersPath = $"/api/cafes/{cafeId}/orders";
        if (method == HttpMethods.Post &&
            path.StartsWith(ordersPath, StringComparison.OrdinalIgnoreCase) &&
            (path.Equals(ordersPath, StringComparison.OrdinalIgnoreCase) ||
             path.Equals($"{ordersPath}/", StringComparison.OrdinalIgnoreCase)))
        {
            var limits = await _platformCatalog.GetLimitsAsync(tier, cancellationToken);
            var maxOrders = limits.MaxOrdersPerDay;
            if (maxOrders == int.MaxValue)
                return (true, null, null);

            var todayStart = DateTime.UtcNow.Date;
            var count = await _db.Orders
                .CountAsync(o => o.CafeId == cafeId && o.CreatedAt >= todayStart, cancellationToken);

            if (count >= maxOrders)
                return (false, "PLAN_LIMIT_REACHED", "Daily order limit reached for your plan. Please upgrade.");
        }

        var customersPath = $"/api/cafes/{cafeId}/customers";
        if (path.StartsWith(customersPath, StringComparison.OrdinalIgnoreCase) &&
            (path.Equals(customersPath, StringComparison.OrdinalIgnoreCase) ||
             path.Equals($"{customersPath}/", StringComparison.OrdinalIgnoreCase)))
        {
            var limitsCustomers = await _platformCatalog.GetLimitsAsync(tier, cancellationToken);
            var maxCustomers = limitsCustomers.MaxCustomers;
            if (maxCustomers == int.MaxValue)
                return (true, null, null);

            var count = await _db.Customers
                .CountAsync(c => c.CafeId == cafeId, cancellationToken);

            if (count >= maxCustomers)
                return (false, "PLAN_LIMIT_REACHED", "Customer limit reached for your plan. Please upgrade.");
        }

        var branchesPath = $"/api/cafes/{cafeId}/branches";
        if (path.StartsWith(branchesPath, StringComparison.OrdinalIgnoreCase) &&
            (path.Equals(branchesPath, StringComparison.OrdinalIgnoreCase) ||
             path.Equals($"{branchesPath}/", StringComparison.OrdinalIgnoreCase)))
        {
            var limitsBranches = await _platformCatalog.GetLimitsAsync(tier, cancellationToken);
            var maxBranches = limitsBranches.MaxBranches;
            if (maxBranches == int.MaxValue)
                return (true, null, null);

            var branchCount = await _db.Branches.CountAsync(b => b.CafeId == cafeId, cancellationToken);
            if (branchCount >= maxBranches)
                return (false, "PLAN_LIMIT_REACHED", "Branch limit reached for your plan. Please upgrade.");
        }

        var smsCampaignPath = $"/api/cafes/{cafeId}/sms/campaign";
        if (path.Equals(smsCampaignPath, StringComparison.OrdinalIgnoreCase) ||
            path.Equals($"{smsCampaignPath}/", StringComparison.OrdinalIgnoreCase))
        {
            var limitsSms = await _platformCatalog.GetLimitsAsync(tier, cancellationToken);
            var maxSms = limitsSms.MaxSmsPerMonth;
            if (maxSms == 0)
                return (false, "PLAN_LIMIT_REACHED", "SMS is not available on the Free plan. Please upgrade.");

            if (maxSms == int.MaxValue)
                return (true, null, null);

            var monthKey = $"sms:usage:{cafeId}:{DateTime.UtcNow:yyyy-MM}";
            var redis = _redis.GetDatabase();
            var used = await redis.StringGetAsync(monthKey);
            var usedCount = used.HasValue ? (int)used : 0;

            if (usedCount >= maxSms)
                return (false, "PLAN_LIMIT_REACHED", "Monthly SMS limit reached for your plan. Please upgrade.");
        }

        return (true, null, null);
    }
}
