using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Json;
using Tiku.Application.Jobs;
using Tiku.Application.Security;
using Tiku.Domain.Commerce;
using Tiku.Domain.Operations;
using Tiku.Domain.Platform;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;

namespace Tiku.IntegrationTests.Api;

public sealed class CapabilityAuthorizationTests
{
    [Fact]
    public async Task Background_job_rechecks_capability_after_enqueue_before_execution()
    {
        await using var factory = new ApiTestFactory();
        var tenantId = Guid.NewGuid();
        await factory.SeedAsync(
            new Tenant { Id = tenantId, Slug = tenantId.ToString("N"), Name = "Job Capability Tenant" },
            new PlatformSaasPlan { Code = "job-capability-test", Name = "Job Capability Test" },
            new PlanModuleEntitlement { PlanCode = "job-capability-test", ModuleCode = "content" },
            new TenantSubscription
            {
                TenantId = tenantId,
                PlanCode = "job-capability-test",
                Status = TenantSubscriptionStatus.Active,
                StartsAt = DateTimeOffset.UtcNow.AddDays(-1),
                ExpiresAt = DateTimeOffset.UtcNow.AddDays(30)
            });

        using var scope = factory.CreateSystemScope("Verify job capability at consumption");
        var jobs = scope.ServiceProvider.GetRequiredService<IBackgroundJobService>();
        var job = await jobs.EnqueueAsync(new CreateBackgroundJobCommand(
            tenantId,
            "content_export",
            JsonSerializer.SerializeToElement(new { exportType = "capability-test" })));

        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        dbContext.TenantModuleOverrides.Add(new TenantModuleOverride
        {
            TenantId = tenantId,
            ModuleCode = "content",
            Mode = TenantModuleOverrideMode.Disabled,
            Reason = "Integration test revocation"
        });
        await dbContext.SaveChangesAsync();

        Assert.True(await jobs.ProcessRequestedAsync(
            job.Id, tenantId, job.JobType, "capability-test-worker"));
        var stored = await dbContext.BackgroundJobs.AsNoTracking().SingleAsync(item => item.Id == job.Id);
        Assert.Equal(BackgroundJobStatus.Failed, stored.Status);
        Assert.Equal("Tenant capability was revoked before job execution.", stored.LastError);
    }

    [Fact]
    public async Task Entitlement_is_database_backed_and_past_due_is_read_only()
    {
        await using var factory = new ApiTestFactory();
        var tenantId = Guid.NewGuid();
        await factory.SeedAsync(
            new Tenant { Id = tenantId, Slug = tenantId.ToString("N"), Name = "Capability Tenant" },
            new PlatformSaasPlan { Code = "capability-test", Name = "Capability Test" },
            new TenantSubscription
            {
                TenantId = tenantId,
                PlanCode = "capability-test",
                Status = TenantSubscriptionStatus.Active,
                StartsAt = DateTimeOffset.UtcNow.AddDays(-1),
                ExpiresAt = DateTimeOffset.UtcNow.AddDays(30)
            });

        using var scope = factory.CreateSystemScope("Verify capability authorization");
        var evaluator = scope.ServiceProvider.GetRequiredService<ICapabilityAccessEvaluator>();
        Assert.False(await evaluator.IsAllowedAsync(tenantId, "content", CapabilityOperation.Read));

        var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        dbContext.PlanModuleEntitlements.Add(new PlanModuleEntitlement
        {
            PlanCode = "capability-test",
            ModuleCode = "content"
        });
        await dbContext.SaveChangesAsync();
        Assert.True(await evaluator.IsAllowedAsync(tenantId, "content", CapabilityOperation.Write));

        var subscription = dbContext.TenantSubscriptions.Single(item => item.TenantId == tenantId);
        subscription.Status = TenantSubscriptionStatus.PastDue;
        await dbContext.SaveChangesAsync();
        Assert.True(await evaluator.IsAllowedAsync(tenantId, "content", CapabilityOperation.Read));
        Assert.False(await evaluator.IsAllowedAsync(tenantId, "content", CapabilityOperation.Write));
    }
}
