using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.Tenancy;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;
using Tiku.IntegrationTests.Api;

namespace Tiku.IntegrationTests;

public sealed class TenantDomainLifecycleTests
{
    [Fact]
    public async Task Domain_becomes_active_only_after_dns_and_tls_are_ready()
    {
        await using var factory = new ApiTestFactory(
            domainOwnershipVerifier: new FakeOwnershipVerifier(true),
            domainGatewayProvisioner: new FakeGatewayProvisioner(true));
        var tenantId = Guid.NewGuid();
        var domainId = Guid.NewGuid();
        await factory.SeedAsync(
            new Tenant
            {
                Id = tenantId,
                Slug = "tenant-a",
                Name = "Tenant A",
                Status = TenantStatus.Active,
                Mode = TenantMode.Saas
            },
            new TenantDomain
            {
                Id = domainId,
                TenantId = tenantId,
                Host = "learn.tenant-a.example",
                DomainType = TenantDomainType.Custom,
                Status = TenantDomainStatus.Pending,
                VerificationToken = "verification-token"
            });

        using (var scope = factory.CreateSystemScope("Process domain lifecycle"))
        {
            var processed = await scope.ServiceProvider
                .GetRequiredService<ITenantDomainLifecycleService>()
                .ProcessPendingAsync();
            Assert.Equal(1, processed);
        }

        using var verificationScope = factory.CreateSystemScope();
        var dbContext = verificationScope.ServiceProvider.GetRequiredService<TikuDbContext>();
        var domain = await dbContext.TenantDomains.SingleAsync(item => item.Id == domainId);
        Assert.Equal(TenantDomainStatus.Active, domain.Status);
        Assert.NotNull(domain.DnsVerifiedAt);
        Assert.NotNull(domain.TlsReadyAt);
        Assert.Null(domain.LastFailureReason);
        Assert.True(await dbContext.AuditLogs.AnyAsync(item =>
            item.TenantId == tenantId && item.Action == "tenant.domain.activated" &&
            item.TargetId == domainId.ToString("N")));
    }

    [Fact]
    public async Task Missing_gateway_configuration_keeps_domain_pending()
    {
        await using var factory = new ApiTestFactory(
            domainOwnershipVerifier: new FakeOwnershipVerifier(true),
            domainGatewayProvisioner: new FakeGatewayProvisioner(false, false));
        var tenantId = Guid.NewGuid();
        var domainId = Guid.NewGuid();
        await factory.SeedAsync(
            new Tenant
            {
                Id = tenantId,
                Slug = "tenant-a",
                Name = "Tenant A",
                Status = TenantStatus.Active,
                Mode = TenantMode.Saas
            },
            new TenantDomain
            {
                Id = domainId,
                TenantId = tenantId,
                Host = "pending.tenant-a.example",
                Status = TenantDomainStatus.Pending,
                VerificationToken = "verification-token"
            });

        using (var scope = factory.CreateSystemScope("Process pending domain"))
        {
            await scope.ServiceProvider.GetRequiredService<ITenantDomainLifecycleService>().ProcessPendingAsync();
        }

        using var verificationScope = factory.CreateSystemScope();
        var domain = await verificationScope.ServiceProvider.GetRequiredService<TikuDbContext>()
            .TenantDomains.SingleAsync(item => item.Id == domainId);
        Assert.Equal(TenantDomainStatus.Pending, domain.Status);
        Assert.NotNull(domain.DnsVerifiedAt);
        Assert.Contains("not configured", domain.LastFailureReason, StringComparison.OrdinalIgnoreCase);
    }

    private sealed class FakeOwnershipVerifier(bool verified) : IDomainOwnershipVerifier
    {
        public Task<DomainOwnershipResult> VerifyAsync(
            string host,
            string verificationToken,
            CancellationToken cancellationToken = default)
        {
            return Task.FromResult(new DomainOwnershipResult(verified, true, verified ? null : "DNS failed"));
        }
    }

    private sealed class FakeGatewayProvisioner(bool ready, bool configured = true) : IDomainGatewayProvisioner
    {
        public Task<DomainGatewayResult> EnsureTlsAsync(
            string host,
            CancellationToken cancellationToken = default)
        {
            return Task.FromResult(new DomainGatewayResult(
                ready,
                configured,
                ready ? null : configured ? "TLS pending" : "Gateway is not configured"));
        }
    }
}