using Microsoft.EntityFrameworkCore;
using Tiku.Application.Commerce;
using Tiku.Application.Tenancy;
using Tiku.Domain.Tenancy;

namespace Tiku.Infrastructure.Commerce;

internal sealed partial class CommerceAdminService
{
    public async Task<IReadOnlyCollection<TenantPaymentProviderItem>> GetPaymentAccountsAsync(
        CommerceAdminActor actor,
        CommerceAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var accounts = await providerConfigService.GetProvidersAsync(
            actor.TenantId,
            TenantExternalProviderCapability.Payment,
            string.IsNullOrWhiteSpace(query.Provider) ? null : NormalizeProvider(query.Provider),
            query.Limit,
            cancellationToken);

        return accounts.Select(ToPaymentAccountItem).ToArray();
    }

    public async Task<TenantPaymentProviderItem> UpsertPaymentAccountAsync(
        CommerceAdminActor actor,
        UpsertPaymentAccountCommand command,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var account = await providerConfigService.UpsertProviderAsync(
            actor.TenantId,
            new UpsertTenantExternalProviderCommand(
                TenantExternalProviderCapability.Payment,
                command.Provider,
                command.Status,
                command.DisplayName,
                command.SecretRef,
                command.Priority,
                WithPaymentMode(command.ConfigPublic, command.Mode),
                JsonObjectOrDefault(default)),
            cancellationToken);

        return ToPaymentAccountItem(account);
    }

    public async Task<TenantSecretItem> UpsertTenantSecretAsync(
        CommerceAdminActor actor,
        UpsertTenantSecretCommand command,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var secretRef = string.IsNullOrWhiteSpace(command.SecretRef)
            ? $"tenant_secrets:{command.Purpose}:{NormalizeProvider(command.Provider)}:{command.SecretKey}"
            : command.SecretRef.Trim();
        var provider = NormalizeProvider(command.Provider);
        var secret = await dbContext.TenantSecrets
            .SingleOrDefaultAsync(item => item.TenantId == actor.TenantId && item.SecretRef == secretRef,
                cancellationToken);
        if (secret is null)
        {
            secret = new TenantSecret
            {
                TenantId = actor.TenantId,
                SecretRef = secretRef
            };
            dbContext.TenantSecrets.Add(secret);
        }
        else
        {
            secret.RotatedAt = DateTimeOffset.UtcNow;
        }

        var protectedPayload = tenantSecretProtector.Protect(
            actor.TenantId,
            secretRef,
            JsonObjectOrDefault(command.SecretPayload));

        secret.Purpose = command.Purpose.Trim();
        secret.Provider = provider;
        secret.SecretKey = command.SecretKey.Trim();
        secret.Status = command.Status;
        secret.EncryptionKeyId = protectedPayload.KeyId;
        secret.EncryptedPayload = protectedPayload.Ciphertext;
        secret.EncryptionNonce = protectedPayload.Nonce;
        secret.EncryptionTag = protectedPayload.Tag;
        secret.ExpiresAt = command.ExpiresAt;

        await dbContext.SaveChangesAsync(cancellationToken);
        return ToSecretItem(secret);
    }
}