using System.Collections.Concurrent; using System.Text; using System.Text.Json; using Alchegos.HCI.Models; namespace Alchegos.HCI.Services; public class InMemoryChatSessionService : IChatSessionService { private readonly ConcurrentDictionary _sessions = new(); private readonly IHttpClientFactory _httpClientFactory; private readonly IConfiguration _configuration; private readonly ILogger _logger; public InMemoryChatSessionService(IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger logger) { _httpClientFactory = httpClientFactory; _configuration = configuration; _logger = logger; } public ChatSession GetOrCreateSession(string sessionId) { return _sessions.GetOrAdd(sessionId, id => new ChatSession { SessionId = id }); } public ChatSession? GetSession(string sessionId) { _sessions.TryGetValue(sessionId, out var session); return session; } public void AddMessage(string sessionId, ChatMessage message) { if (_sessions.TryGetValue(sessionId, out var session)) { session.Messages.Add(message); } } public void SetWaitingStatus(string sessionId, bool isWaiting) { if (_sessions.TryGetValue(sessionId, out var session)) { session.IsWaitingForResponse = isWaiting; } } public void RemoveSession(string sessionId) { if (_sessions.TryRemove(sessionId, out var session)) _logger.LogInformation("Session removed: {SessionId}", sessionId); else _logger.LogWarning("Attempted to remove non-existent session: {SessionId}", sessionId); } public void RegisterSessionCallbacks(string sessionId, Func? messageCallback, Func? closeCallback) { if (_sessions.TryGetValue(sessionId, out var session)) { session.NotifyMessageReceived = messageCallback; session.NotifySessionClosed = closeCallback; _logger.LogInformation("Registered callbacks for session: {SessionId}", sessionId); } else _logger.LogWarning("Attempted to register callbacks for non-existent session: {SessionId}", sessionId); } public void UnregisterSessionCallbacks(string sessionId) { if (_sessions.TryGetValue(sessionId, out var session)) { session.NotifyMessageReceived = null; session.NotifySessionClosed = null; _logger.LogInformation("Unregistered callbacks for session: {SessionId}", sessionId); } } public async Task SendMessageToExternalWebhook(string webhookUrl, string sessionId, string chatInput) { //var webhookUrl = Environment.GetEnvironmentVariable("HCI_WEBHOOK_URL"); if (string.IsNullOrEmpty(webhookUrl)) { _logger.LogError("ExternalWebhookUrl is not configured in ChatSettings."); SetWaitingStatus(sessionId, false); return; } var payload = new { sessionId, chatInput }; var jsonPayload = JsonSerializer.Serialize(payload); var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); try { var httpClient = _httpClientFactory.CreateClient(); _logger.LogInformation("Sending message to webhook for session {SessionId}: {Url}", sessionId, webhookUrl); var response = await httpClient.PostAsync(webhookUrl, content); response.EnsureSuccessStatusCode(); _logger.LogInformation("Successfully sent message to webhook for session {SessionId}", sessionId); } catch (HttpRequestException ex) { _logger.LogError(ex, "Error sending message to external webhook for session {SessionId}", sessionId); AddMessage(sessionId, new ChatMessage { IsUser = false, Content = $"Error sending message: {ex.Message}" }); SetWaitingStatus(sessionId, false); await TriggerMessageReceived(sessionId, $"Error: Could not reach the processing service."); } catch (Exception ex) { _logger.LogError(ex, "Unexpected error sending message to external webhook for session {SessionId}", sessionId); AddMessage(sessionId, new ChatMessage { IsUser = false, Content = $"An unexpected error occurred: {ex.Message}" }); SetWaitingStatus(sessionId, false); await TriggerMessageReceived(sessionId, $"Error: An unexpected error occurred."); } } public async Task TriggerMessageReceived(string sessionId, string output) { if (_sessions.TryGetValue(sessionId, out var session)) { _logger.LogInformation("Received message via webhook for session {SessionId}", sessionId); var serverMessage = new ChatMessage { IsUser = false, Content = output }; session.Messages.Add(serverMessage); session.IsWaitingForResponse = false; if (session.NotifyMessageReceived != null) await session.NotifyMessageReceived(serverMessage); else _logger.LogWarning("No message callback registered for session {SessionId} to notify.", sessionId); } else _logger.LogWarning("Received message for non-existent or inactive session: {SessionId}", sessionId); } public async Task TriggerSessionClosed(string sessionId) { if (_sessions.TryGetValue(sessionId, out var session)) { _logger.LogInformation("Received close request via webhook for session {SessionId}", sessionId); if (session.NotifySessionClosed != null) await session.NotifySessionClosed(); else _logger.LogWarning("No close callback registered for session {SessionId} to notify.", sessionId); RemoveSession(sessionId); } else _logger.LogWarning("Received close request for non-existent or already removed session: {SessionId}", sessionId); } }