using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.Security;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;

namespace Tiku.IntegrationTests;

public sealed class SystemScopeAuditTests
{
    [Fact]
    public async Task Descriptor_is_required_and_success_is_audited()
    {
        await using var factory = new Api.ApiTestFactory();
        using var outer = factory.CreateSystemScope("Resolve execution scope");
        var executionScope = outer.ServiceProvider.GetRequiredService<ITenantExecutionScope>();
        await Assert.ThrowsAsync<ArgumentException>(() => executionScope.ExecuteAsync(
            new SystemScopeRequest(null, SystemScopeCallerType.Worker, "worker", "", "job-1"),
            (_, _) => Task.CompletedTask));
        await Assert.ThrowsAsync<ArgumentException>(() => executionScope.ExecuteAsync(
            new SystemScopeRequest(null, SystemScopeCallerType.Worker, "worker", "missing target", "job-2"),
            (_, _) => Task.CompletedTask));

        var correlationId = Guid.NewGuid().ToString("N");
        var tenantId = Guid.NewGuid();
        await factory.SeedAsync(new Tenant
        {
            Id = tenantId,
            Slug = tenantId.ToString("N"),
            Name = "System Scope Tenant"
        });
        await executionScope.ExecuteAsync(
            new SystemScopeRequest(tenantId, SystemScopeCallerType.Worker, "background-worker", "test audit", correlationId),
            (_, _) => Task.CompletedTask);

        using var verification = factory.CreateSystemScope("Verify execution scope audit");
        var dbContext = verification.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.Contains(dbContext.AuditLogs, item => item.Action == "system_scope.entered" && item.TargetId == correlationId);
        Assert.Contains(dbContext.AuditLogs, item => item.Action == "system_scope.completed" && item.TargetId == correlationId);
    }

    [Fact]
    public async Task Failed_system_scope_rolls_back_business_write_and_persists_failure_audit()
    {
        await using var factory = new Api.ApiTestFactory();
        using var outer = factory.CreateSystemScope("Resolve execution scope");
        var executionScope = outer.ServiceProvider.GetRequiredService<ITenantExecutionScope>();
        var correlationId = Guid.NewGuid().ToString("N");
        var tenantId = Guid.NewGuid();
        await factory.SeedAsync(new Tenant
        {
            Id = tenantId,
            Slug = tenantId.ToString("N"),
            Name = "Existing Tenant"
        });

        await Assert.ThrowsAsync<InvalidOperationException>(() => executionScope.ExecuteAsync(
            new SystemScopeRequest(
                tenantId,
                SystemScopeCallerType.Worker,
                "background-worker",
                "verify transactional rollback",
                correlationId),
            async (provider, cancellationToken) =>
            {
                var dbContext = provider.GetRequiredService<TikuDbContext>();
                dbContext.TenantBrandings.Add(new TenantBranding
                {
                    TenantId = tenantId,
                    BrandName = "Must Roll Back"
                });
                await dbContext.SaveChangesAsync(cancellationToken);
                throw new InvalidOperationException("expected failure");
            }));

        using var verification = factory.CreateSystemScope("Verify failed execution scope audit");
        var verificationDb = verification.ServiceProvider.GetRequiredService<TikuDbContext>();
        Assert.DoesNotContain(verificationDb.TenantBrandings, item => item.TenantId == tenantId);
        Assert.Contains(verificationDb.AuditLogs, item =>
            item.Action == "system_scope.entered" && item.TargetId == correlationId);
        Assert.Contains(verificationDb.AuditLogs, item =>
            item.Action == "system_scope.failed" && item.TargetId == correlationId);
        Assert.DoesNotContain(verificationDb.AuditLogs, item =>
            item.Action == "system_scope.completed" && item.TargetId == correlationId);
    }
}
