using Deal.Grpc.Telegram;
using Deal.Infrastructure.Integrations.Options;
using Grpc.Core;
using Grpc.Net.Client;
namespace Deal.Infrastructure.Integrations.Models;
///
/// Транспорт gRPC-клиента telegram-service
///
public sealed class TelegramGrpcConnection : IDisposable
{
///
/// Env-ключ ожидаемого service-token.
///
public const string ServiceTokenEnvKey = "DEAL_SERVICE_TOKEN";
///
/// Ключ gRPC-metadata с tenant-id.
///
public const string TenantIdMetadataKey = "tenant-id";
///
/// Ключ gRPC-metadata с service-token.
///
public const string ServiceTokenMetadataKey = "service-token";
private readonly GrpcChannel _channel;
private readonly string _serviceToken;
///
/// Создаёт транспорт telegram-service по конфигурации и env-токену
///
/// Конфигурация секции Services:Telegram (endpoint).
/// Сертификаты mTLS: null — plaintext-канал (dev, флаг выключен).
/// Пустой endpoint или пустой DEAL_SERVICE_TOKEN.
public TelegramGrpcConnection(TelegramServiceOptions options, MtlsCertificates? mtlsCertificates = null)
{
ArgumentNullException.ThrowIfNull(options);
if (string.IsNullOrWhiteSpace(options.Endpoint))
{
throw new InvalidOperationException(
$"TelegramGrpcConnection: не задан endpoint telegram-service (секция \"{TelegramServiceOptions.SectionName}:Endpoint\").");
}
_serviceToken = Environment.GetEnvironmentVariable(ServiceTokenEnvKey) ?? string.Empty;
if (_serviceToken.Length == 0)
{
throw new InvalidOperationException(
$"TelegramGrpcConnection: не задан env {ServiceTokenEnvKey} — telegram-service отвергнет вызовы (fail-closed, Ruling 1).");
}
GrpcChannelOptions channelOptions = new() { MaxRetryAttempts = 0 };
if (mtlsCertificates is not null)
{
channelOptions.HttpHandler = mtlsCertificates.CreateClientHttpHandler();
}
_channel = GrpcChannel.ForAddress(options.Endpoint, channelOptions);
}
///
/// Создаёт клиент RPC TelegramService поверх общего канала
///
/// Клиент сервиса Telegram (команды ядра наружу).
public TelegramService.TelegramServiceClient CreateClient() => new(_channel);
///
/// Собирает обязательные metadata вызова
///
/// Id тенанта (строка, формат N — как в сессиях telegram-service).
/// Metadata для CallOptions вызова.
public Metadata CreateMetadata(string tenantId)
{
var metadata = new Metadata
{
{ TenantIdMetadataKey, tenantId },
{ ServiceTokenMetadataKey, _serviceToken },
};
return metadata;
}
///
public void Dispose() => _channel.Dispose();
}