using Microsoft.EntityFrameworkCore;
using Tiku.Application.Security;
using Tiku.Domain.Commerce;
using Tiku.Domain.Platform;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;

namespace Tiku.Infrastructure.Security;

internal sealed class CapabilityAccessEvaluator(TikuDbContext dbContext) : ICapabilityAccessEvaluator
{
    public async Task<bool> IsAllowedAsync(
        Guid tenantId,
        string moduleCode,
        CapabilityOperation operation,
        CancellationToken cancellationToken = default)
    {
        var normalized = moduleCode.Trim().ToLowerInvariant();
        if (!ProductModuleCatalog.Contains(normalized))
        {
            return false;
        }
        var moduleExists = await dbContext.ProductModules.AsNoTracking()
            .AnyAsync(item => item.Code == normalized && item.Status == ProductModuleStatus.Active, cancellationToken);
        if (!moduleExists)
        {
            return false;
        }

        var tenantActive = await dbContext.Tenants.AsNoTracking()
            .AnyAsync(item => item.Id == tenantId && item.Status == TenantStatus.Active, cancellationToken);
        if (!tenantActive)
        {
            return false;
        }

        var now = DateTimeOffset.UtcNow;
        var overrideMode = await dbContext.TenantModuleOverrides.AsNoTracking()
            .Where(item => item.TenantId == tenantId && item.ModuleCode == normalized &&
                           (item.ExpiresAt == null || item.ExpiresAt > now))
            .Select(item => (TenantModuleOverrideMode?)item.Mode)
            .SingleOrDefaultAsync(cancellationToken);
        if (overrideMode == TenantModuleOverrideMode.Disabled)
        {
            return false;
        }

        var subscription = await dbContext.TenantSubscriptions.AsNoTracking()
            .Where(item => item.TenantId == tenantId)
            .OrderByDescending(item => item.UpdatedAt)
            .Select(item => new { item.PlanCode, item.Status, item.StartsAt, item.ExpiresAt })
            .FirstOrDefaultAsync(cancellationToken);
        if (subscription is null || subscription.StartsAt > now || subscription.ExpiresAt <= now)
        {
            return false;
        }

        var entitled = overrideMode == TenantModuleOverrideMode.Enabled ||
            await dbContext.PlanModuleEntitlements.AsNoTracking().AnyAsync(
                item => item.PlanCode == subscription.PlanCode && item.ModuleCode == normalized && item.Enabled,
                cancellationToken);
        if (!entitled)
        {
            return false;
        }

        return operation == CapabilityOperation.Read ||
               subscription.Status is TenantSubscriptionStatus.Trial or TenantSubscriptionStatus.Active;
    }

    public async Task<IReadOnlySet<string>> GetEnabledModulesAsync(
        Guid tenantId,
        CapabilityOperation operation = CapabilityOperation.Read,
        CancellationToken cancellationToken = default)
    {
        var modules = await dbContext.ProductModules.AsNoTracking()
            .Where(item => item.Status == ProductModuleStatus.Active)
            .Select(item => item.Code)
            .ToArrayAsync(cancellationToken);
        var enabled = new HashSet<string>(StringComparer.Ordinal);
        foreach (var module in modules)
        {
            if (await IsAllowedAsync(tenantId, module, operation, cancellationToken))
            {
                enabled.Add(module);
            }
        }
        return enabled;
    }
}
