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

namespace Tiku.Infrastructure.Tenancy;

internal sealed class TenantOnboardingService(
    TikuDbContext dbContext,
    IFeatureAccessService featureAccessService) : ITenantOnboardingService
{
    public async Task<TenantOnboardingStatus> GetStatusAsync(
        Guid tenantId,
        CancellationToken cancellationToken = default)
    {
        var tenant = await dbContext.Tenants.AsNoTracking()
                         .SingleOrDefaultAsync(value => value.Id == tenantId, cancellationToken)
                     ?? throw new TenantExternalProviderException("Tenant was not found.", "tenant_not_found");
        var ownerActivated = tenant.OwnerUserId.HasValue && await (
            from membership in dbContext.TenantMemberships.AsNoTracking()
            join user in dbContext.Users.AsNoTracking() on membership.UserId equals user.Id
            where membership.TenantId == tenantId && membership.UserId == tenant.OwnerUserId &&
                  membership.Role == TenantRole.TenantOwner && membership.Status == MembershipStatus.Active &&
                  user.Status == UserStatus.Active && !user.ForcePasswordChange
            select membership.Id).AnyAsync(cancellationToken);
        var now = DateTimeOffset.UtcNow;
        var subscriptionActive = await dbContext.TenantSaasSubscriptions.AsNoTracking().AnyAsync(value =>
            value.TenantId == tenantId &&
            (value.Status == TenantSaasSubscriptionStatus.Trial ||
             value.Status == TenantSaasSubscriptionStatus.Active) &&
            value.StartsAt <= now && value.CurrentPeriodEnd > now, cancellationToken);
        var billingPolicyConfigured = await dbContext.TenantBillingPolicies.AsNoTracking().AnyAsync(value =>
            value.TenantId == tenantId && value.RenewalLeadDays >= 1 && value.RenewalLeadDays <= 90 &&
            value.DefaultPaymentProvider != string.Empty, cancellationToken);
        var primaryDomainActive = await dbContext.TenantDomains.AsNoTracking().AnyAsync(value =>
                value.TenantId == tenantId && value.IsPrimary && value.Status == TenantDomainStatus.Active,
            cancellationToken);
        var frontendPublished = await dbContext.TenantFrontendConfigs.AsNoTracking().AnyAsync(value =>
            value.TenantId == tenantId && value.PublishedAt != null, cancellationToken);
        var loginMethods = await dbContext.TenantAuthPolicies.AsNoTracking()
            .Where(value => value.TenantId == tenantId)
            .Select(value => value.AllowedStudentLoginMethods)
            .SingleOrDefaultAsync(cancellationToken) ?? ["password"];
        var hasLoginMethod = loginMethods.Any(value => !string.IsNullOrWhiteSpace(value));

        var enabledFeatures =
            await featureAccessService.GetEnabledFeaturesAsync(tenantId, FeatureAccessOperation.Read,
                cancellationToken);
        var paymentRequired = enabledFeatures.Contains(SaasFeatureCatalog.StudentStore);
        var storageRequired = enabledFeatures.Overlaps(new[]
        {
            SaasFeatureCatalog.PrivateQuestionBank,
            SaasFeatureCatalog.Video,
            SaasFeatureCatalog.Handbook
        });
        var smsRequired = loginMethods.Any(value => string.Equals(value, "sms", StringComparison.OrdinalIgnoreCase));
        var providers = await dbContext.TenantExternalProviders.AsNoTracking()
            .Where(value => value.TenantId == tenantId && value.Status == TenantExternalProviderStatus.Active)
            .Select(value => value.Capability)
            .Distinct()
            .ToArrayAsync(cancellationToken);

        var steps = new List<TenantOnboardingStep>
        {
            new("tenant_active", true, tenant.Status == TenantStatus.Active, tenant.Status.ToString()),
            new("owner_activated", true, ownerActivated, tenant.OwnerUserId?.ToString()),
            new("subscription_active", true, subscriptionActive, null),
            new("billing_policy_configured", true, billingPolicyConfigured, null),
            new("primary_domain_active", true, primaryDomainActive, null),
            new("frontend_config_published", true, frontendPublished, null),
            new("student_login_configured", true, hasLoginMethod, string.Join(',', loginMethods)),
            ProviderStep("object_storage_provider", TenantExternalProviderCapability.ObjectStorage, storageRequired,
                providers),
            ProviderStep("sms_provider", TenantExternalProviderCapability.Sms, smsRequired, providers),
            ProviderStep("payment_provider", TenantExternalProviderCapability.Payment, paymentRequired, providers),
            ProviderStep("notification_provider", TenantExternalProviderCapability.Notification, false, providers)
        };
        var required = steps.Count(value => value.Required);
        var completed = steps.Count(value => value.Required && value.Completed);
        return new TenantOnboardingStatus(tenantId, completed == required, completed, required, steps);
    }

    private static TenantOnboardingStep ProviderStep(
        string code,
        TenantExternalProviderCapability capability,
        bool required,
        IReadOnlyCollection<TenantExternalProviderCapability> providers)
    {
        return new TenantOnboardingStep(code, required, providers.Contains(capability),
            providers.Contains(capability) ? "active" : "missing");
    }
}