using FrameworkDAL.DTO.EIPConversation; using GB5Shared.DTO.Framework.Login; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System; using System.Linq; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace FrameworkBLL.EIPConversation.EIPHandlers.ChannelHandler { public class TeamsChannelHandler : IEIPChannelHandler { // Interface property public string ChannelName => "TEAMS"; private readonly ILogger _logger; private readonly IConfiguration _configuration; private readonly HttpClient _httpClient; public TeamsChannelHandler( ILogger logger, IConfiguration configuration, HttpClient httpClient) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); } // Single CanHandle implementation public bool CanHandle(string channel) => string.Equals(channel, ChannelName, StringComparison.OrdinalIgnoreCase); public async Task SendAsync( EIPResponseContext context, LoginDTO loginDTO, CancellationToken cancellationToken) { if (context == null) throw new ArgumentNullException(nameof(context)); if (loginDTO == null) throw new ArgumentNullException(nameof(loginDTO)); var webhookUrl = _configuration["MessageHubSettings:Teams:WebhookUrl"]; if (string.IsNullOrWhiteSpace(webhookUrl)) throw new InvalidOperationException("Teams WebhookUrl not configured."); var stopwatch = System.Diagnostics.Stopwatch.StartNew(); try { cancellationToken.ThrowIfCancellationRequested(); _logger.LogInformation( "TEAMS SEND START | Tenant={Tenant} | Recipient={Recipient}", context.TenantId, context.Recipient); // -------------------------------------------------- // Build Teams Message Card // -------------------------------------------------- var payload = new { type = "message", attachments = new[] { new { contentType = "application/vnd.microsoft.card.adaptive", content = new { type = "AdaptiveCard", version = "1.4", body = new object[] { new { type = "TextBlock", text = context.Message, wrap = true } }, actions = context.Buttons?.Select(b => new { type = "Action.OpenUrl", title = b.Title, url = b.Url }).ToArray() } } } }; var json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(webhookUrl, content, cancellationToken); stopwatch.Stop(); if (!response.IsSuccessStatusCode) { var error = await response.Content.ReadAsStringAsync(cancellationToken); _logger.LogError( "TEAMS SEND FAILED | Tenant={Tenant} | StatusCode={StatusCode} | Response={Response}", context.TenantId, response.StatusCode, error); return new EIPChannelResponseDTO { IsSent = false, Status = "FAILED", ErrorMessage = error }; } _logger.LogInformation( "TEAMS SEND SUCCESS | Tenant={Tenant} | DurationMs={Duration}", context.TenantId, stopwatch.ElapsedMilliseconds); return new EIPChannelResponseDTO { IsSent = true, ExternalMessageId = Guid.NewGuid().ToString(), Status = "SENT" }; } catch (OperationCanceledException) { stopwatch.Stop(); _logger.LogWarning( "TEAMS SEND CANCELLED | Tenant={Tenant} | DurationMs={Duration}", context?.TenantId, stopwatch.ElapsedMilliseconds); throw; } catch (Exception ex) { stopwatch.Stop(); _logger.LogError(ex, "TEAMS SEND ERROR | Tenant={Tenant} | DurationMs={Duration}", context?.TenantId, stopwatch.ElapsedMilliseconds); return new EIPChannelResponseDTO { IsSent = false, Status = "FAILED", ErrorMessage = ex.Message }; } } } }