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<string, ChatSession> _sessions = new();
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IConfiguration _configuration;
    private readonly ILogger<InMemoryChatSessionService> _logger;

    public InMemoryChatSessionService(IHttpClientFactory httpClientFactory, IConfiguration configuration,
        ILogger<InMemoryChatSessionService> 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<ChatMessage, Task>? messageCallback,
        Func<Task>? 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 sessionId, string chatInput)
    {
        var webhookUrl = _configuration["ChatSettings:ExternalWebhookUrl"];
        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);
    }
}