using FrameworkDAL.DTO.EIPConversation; using GB5Shared.DTO.Framework.Login; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; 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 SlackChannelHandler : IEIPChannelHandler { // Interface property public string ChannelName => "SLACK"; private readonly ILogger _logger; private readonly IConfiguration _configuration; private readonly HttpClient _httpClient; public SlackChannelHandler( 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["Slack:WebhookUrl"]; var defaultChannel = _configuration["Slack:DefaultChannel"]; var defaultUserName = _configuration["Slack:DefaultUserName"]; var defaultIcon = _configuration["Slack:DefaultIconEmoji"]; if (string.IsNullOrWhiteSpace(webhookUrl)) throw new InvalidOperationException("Slack WebhookUrl not configured."); var stopwatch = System.Diagnostics.Stopwatch.StartNew(); try { cancellationToken.ThrowIfCancellationRequested(); _logger.LogInformation( "SLACK SEND START | Tenant={Tenant} | Channel={Channel}", context.TenantId, defaultChannel); // Build Slack Block Kit message var payload = new { channel = defaultChannel, username = defaultUserName, icon_emoji = defaultIcon, blocks = new object[] { new { type = "section", text = new { type = "mrkdwn", text = context.Message } } } }; var json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync( webhookUrl, content, cancellationToken); stopwatch.Stop(); var responseContent = await response.Content.ReadAsStringAsync(cancellationToken); if (!response.IsSuccessStatusCode) { _logger.LogError( "SLACK SEND FAILED | Tenant={Tenant} | StatusCode={StatusCode} | Response={Response}", context.TenantId, response.StatusCode, responseContent); return new EIPChannelResponseDTO { IsSent = false, Status = "FAILED", ErrorMessage = responseContent }; } _logger.LogInformation( "SLACK SEND SUCCESS | Tenant={Tenant} | DurationMs={Duration}", context.TenantId, stopwatch.ElapsedMilliseconds); return new EIPChannelResponseDTO { IsSent = true, ExternalMessageId = Guid.NewGuid().ToString(), Status = "SENT" }; } catch (OperationCanceledException) { _logger.LogWarning( "SLACK SEND CANCELLED | Tenant={Tenant}", context?.TenantId); throw; } catch (Exception ex) { stopwatch.Stop(); _logger.LogError(ex, "SLACK SEND ERROR | Tenant={Tenant} | DurationMs={Duration}", context?.TenantId, stopwatch.ElapsedMilliseconds); return new EIPChannelResponseDTO { IsSent = false, Status = "FAILED", ErrorMessage = ex.Message }; } } } }