using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Catalog;
using Tiku.Application.Content;
using Tiku.Application.Notifications;
using Tiku.Application.Tenancy;
using Tiku.Application.TenantAdmin;
using Tiku.Domain.Learning;
using Tiku.Domain.Operations;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Security;
using Tiku.Infrastructure.Tenancy;

namespace Tiku.Infrastructure.TenantAdmin;

internal sealed class TenantDomainProviderService(TenantAdminServiceDependencies dependencies)
    : TenantAdminServiceBase(dependencies), ITenantDomainProviderService
{
    public async Task<CatalogList<TenantDomainItem>> GetDomainsAsync(
        TenantAdminActor actor,
        CancellationToken cancellationToken = default)
    {
        await RequireAllDataScopeAsync(actor, cancellationToken);
        var items = await tenancyPersistence.TenantDomains.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId)
            .OrderByDescending(item => item.IsPrimary)
            .ThenBy(item => item.CreatedAt)
            .Select(item => ToDomainItem(item))
            .ToArrayAsync(cancellationToken);
        return new CatalogList<TenantDomainItem>(items);
    }

    public async Task<ContentManagementResult<TenantDomainItem>> CreateDomainAsync(
        TenantAdminActor actor,
        CreateTenantDomainCommand command,
        CancellationToken cancellationToken = default)
    {
        await RequireAllDataScopeAsync(actor, cancellationToken);
        TenantDomain generated;
        try
        {
            generated = TenantDomainProvisioning.CreatePrimary(actor.TenantId, command.Host);
        }
        catch (ArgumentException exception)
        {
            throw new TenantAdminDirectException(exception.Message, "invalid_domain_host");
        }

        var host = generated.Host;
        if (command.IsPrimary)
        {
            var primaryDomains = await tenancyPersistence.TenantDomains
                .Where(item => item.TenantId == actor.TenantId && item.IsPrimary)
                .ToArrayAsync(cancellationToken);
            foreach (var domain in primaryDomains) domain.IsPrimary = false;
        }

        var item = new TenantDomain
        {
            TenantId = actor.TenantId,
            Host = host,
            DomainType = ParseEnum(command.DomainType, TenantDomainType.Custom, "invalid_domain_type"),
            Status = TenantDomainStatus.Pending,
            IsPrimary = command.IsPrimary,
            VerificationToken = generated.VerificationToken
        };
        tenancyPersistence.TenantDomains.Add(item);
        await AddAuditAsync(actor, "tenant.domain.created", "tenant_domains", item.Id, cancellationToken);
        await unitOfWork.SaveChangesAsync(cancellationToken);
        return new ContentManagementResult<TenantDomainItem>(ToDomainItem(item));
    }

    public async Task<CatalogList<TenantIdentityProviderItem>> GetAuthProvidersAsync(
        TenantAdminActor actor,
        CancellationToken cancellationToken = default)
    {
        await RequireAllDataScopeAsync(actor, cancellationToken);
        var items = await providerConfigService.GetProvidersAsync(
            actor.TenantId,
            TenantExternalProviderCapability.Identity,
            cancellationToken: cancellationToken);
        return new CatalogList<TenantIdentityProviderItem>(items.Select(ToAuthProviderItem).ToArray());
    }

    public async Task<ContentManagementResult<TenantIdentityProviderItem>> UpsertAuthProviderAsync(
        TenantAdminActor actor,
        UpsertTenantIdentityProviderCommand command,
        CancellationToken cancellationToken = default)
    {
        await RequireAllDataScopeAsync(actor, cancellationToken);
        ArgumentException.ThrowIfNullOrWhiteSpace(command.Provider);
        var item = await providerConfigService.UpsertProviderAsync(
            actor.TenantId,
            new UpsertTenantExternalProviderCommand(
                TenantExternalProviderCapability.Identity,
                command.Provider,
                ParseEnum(command.Status, TenantExternalProviderStatus.Disabled, "invalid_auth_provider_status"),
                command.DisplayName,
                command.SecretRef,
                command.Priority,
                command.ConfigPublic,
                JsonObjectOrDefault(default)),
            cancellationToken);

        await AddAuditAsync(actor, "tenant.auth_provider.upserted", "tenant_external_providers", item.Id,
            cancellationToken);
        await unitOfWork.SaveChangesAsync(cancellationToken);
        return new ContentManagementResult<TenantIdentityProviderItem>(ToAuthProviderItem(item));
    }
}

