using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using ChatService.Domain.AggregatesModel.ConversationAggregate;
using ChatService.Domain.Contracts;

namespace ChatService.API.Hubs;

/// <summary>
/// EN: Main SignalR hub for real-time chat functionality.
/// VI: SignalR hub chính cho chức năng chat thời gian thực.
/// </summary>
[Authorize]
public class ChatHub : Hub<IChatHubClient>
{
    private readonly IConversationRepository _conversationRepository;
    private readonly IAIService _aiService;
    private readonly ILogger<ChatHub> _logger;

    // EN: Track active connections per user / VI: Theo dõi kết nối active theo user
    private static readonly Dictionary<string, HashSet<string>> _userConnections = new();
    private static readonly object _lock = new();

    public ChatHub(
        IConversationRepository conversationRepository,
        IAIService aiService,
        ILogger<ChatHub> logger)
    {
        _conversationRepository = conversationRepository;
        _aiService = aiService;
        _logger = logger;
    }

    #region Connection Lifecycle

    /// <summary>
    /// EN: Called when a new connection is established.
    /// VI: Được gọi khi kết nối mới được thiết lập.
    /// </summary>
    public override async Task OnConnectedAsync()
    {
        var userId = Context.UserIdentifier;
        if (string.IsNullOrEmpty(userId))
        {
            _logger.LogWarning("Connection attempted without user identifier");
            await base.OnConnectedAsync();
            return;
        }

        _logger.LogInformation("User {UserId} connected with connection {ConnectionId}", 
            userId, Context.ConnectionId);

        // EN: Track user connection / VI: Theo dõi kết nối user
        lock (_lock)
        {
            if (!_userConnections.ContainsKey(userId))
            {
                _userConnections[userId] = new HashSet<string>();
            }
            _userConnections[userId].Add(Context.ConnectionId);
        }

        // EN: Join user's conversation rooms / VI: Tham gia các phòng hội thoại của user
        try
        {
            var conversations = await _conversationRepository.GetUserConversationsAsync(
                Guid.Parse(userId), 
                0, 
                100, // Max rooms to auto-join
                CancellationToken.None);

            foreach (var conv in conversations)
            {
                await Groups.AddToGroupAsync(Context.ConnectionId, conv.Id.ToString());
                _logger.LogDebug("User {UserId} auto-joined room {RoomId}", userId, conv.Id);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to auto-join rooms for user {UserId}", userId);
        }

        // EN: Notify others that user is online / VI: Thông báo user đã online
        await Clients.Others.UserStatusChanged(userId, true, null);

        await base.OnConnectedAsync();
    }

    /// <summary>
    /// EN: Called when a connection is terminated.
    /// VI: Được gọi khi kết nối bị ngắt.
    /// </summary>
    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        var userId = Context.UserIdentifier;
        if (!string.IsNullOrEmpty(userId))
        {
            bool isLastConnection;
            lock (_lock)
            {
                if (_userConnections.TryGetValue(userId, out var connections))
                {
                    connections.Remove(Context.ConnectionId);
                    isLastConnection = connections.Count == 0;
                    if (isLastConnection)
                    {
                        _userConnections.Remove(userId);
                    }
                }
                else
                {
                    isLastConnection = true;
                }
            }

            if (isLastConnection)
            {
                // EN: User is completely offline / VI: User hoàn toàn offline
                await Clients.Others.UserStatusChanged(userId, false, DateTime.UtcNow);
                _logger.LogInformation("User {UserId} is now offline", userId);
            }
        }

        if (exception != null)
        {
            _logger.LogError(exception, "Connection {ConnectionId} disconnected with error", 
                Context.ConnectionId);
        }

        await base.OnDisconnectedAsync(exception);
    }

    #endregion

    #region Room Management

    /// <summary>
    /// EN: Join a specific chat room/conversation.
    /// VI: Tham gia phòng chat/cuộc hội thoại cụ thể.
    /// </summary>
    public async Task JoinRoom(Guid roomId)
    {
        var userId = Context.UserIdentifier ?? throw new HubException("User not authenticated");
        
        _logger.LogInformation("User {UserId} joining room {RoomId}", userId, roomId);
        
        await Groups.AddToGroupAsync(Context.ConnectionId, roomId.ToString());
        
        // EN: Get user name from claims / VI: Lấy tên user từ claims
        var userName = Context.User?.FindFirst("name")?.Value 
            ?? Context.User?.FindFirst("preferred_username")?.Value 
            ?? userId;
        
        // EN: Notify other members / VI: Thông báo các thành viên khác
        await Clients.OthersInGroup(roomId.ToString())
            .UserJoined(userId, userName, roomId);
    }

    /// <summary>
    /// EN: Leave a specific chat room/conversation.
    /// VI: Rời phòng chat/cuộc hội thoại cụ thể.
    /// </summary>
    public async Task LeaveRoom(Guid roomId)
    {
        var userId = Context.UserIdentifier ?? throw new HubException("User not authenticated");
        
        _logger.LogInformation("User {UserId} leaving room {RoomId}", userId, roomId);
        
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomId.ToString());
        
        // EN: Notify other members / VI: Thông báo các thành viên khác
        await Clients.OthersInGroup(roomId.ToString())
            .UserLeft(userId, roomId);
    }

    #endregion

    #region Messaging

    /// <summary>
    /// EN: Send a message to a room. The message is saved and broadcast to all room members.
    /// VI: Gửi tin nhắn đến phòng. Tin nhắn được lưu và broadcast đến tất cả thành viên.
    /// </summary>
    public async Task SendMessage(Guid roomId, string content, string? messageType = null)
    {
        var userId = Context.UserIdentifier ?? throw new HubException("User not authenticated");
        
        if (string.IsNullOrWhiteSpace(content))
        {
            throw new HubException("Message content cannot be empty");
        }

        _logger.LogInformation("User {UserId} sending message to room {RoomId}", userId, roomId);

        // EN: Get user info from claims / VI: Lấy thông tin user từ claims
        var userName = Context.User?.FindFirst("name")?.Value 
            ?? Context.User?.FindFirst("preferred_username")?.Value 
            ?? "Unknown";

        // EN: Create message notification / VI: Tạo thông báo tin nhắn
        var messageId = Guid.NewGuid();
        var notification = new MessageNotification(
            Id: messageId,
            ConversationId: roomId,
            SenderId: userId,
            SenderName: userName,
            Content: content,
            MessageType: messageType ?? "text",
            SentAt: DateTime.UtcNow
        );

        // EN: Broadcast to room / VI: Broadcast đến phòng
        await Clients.Group(roomId.ToString()).ReceiveMessage(notification);

        // EN: Check for AI trigger / VI: Kiểm tra AI trigger
        if (content.StartsWith("@gpt ", StringComparison.OrdinalIgnoreCase))
        {
            var prompt = content[5..].Trim();
            if (!string.IsNullOrEmpty(prompt))
            {
                // EN: Start streaming AI response in background
                // VI: Bắt đầu streaming AI response trong background
                _ = StreamAIResponseToRoom(roomId, prompt);
            }
        }
    }

    /// <summary>
    /// EN: Send typing indicator to a room.
    /// VI: Gửi typing indicator đến phòng.
    /// </summary>
    public async Task SendTypingIndicator(Guid roomId, bool isTyping)
    {
        var userId = Context.UserIdentifier ?? throw new HubException("User not authenticated");
        
        var userName = Context.User?.FindFirst("name")?.Value 
            ?? Context.User?.FindFirst("preferred_username")?.Value 
            ?? "Unknown";

        await Clients.OthersInGroup(roomId.ToString())
            .TypingIndicator(userId, userName, roomId, isTyping);
    }

    /// <summary>
    /// EN: Mark a message as read.
    /// VI: Đánh dấu tin nhắn đã đọc.
    /// </summary>
    public async Task MarkMessageRead(Guid roomId, Guid messageId)
    {
        var userId = Context.UserIdentifier ?? throw new HubException("User not authenticated");
        
        await Clients.OthersInGroup(roomId.ToString())
            .MessageRead(userId, messageId, roomId);
    }

    #endregion

    #region AI Integration

    /// <summary>
    /// EN: Stream AI response to the caller (invokable from client).
    /// VI: Stream AI response đến caller (có thể gọi từ client).
    /// </summary>
    public async IAsyncEnumerable<string> StreamAIResponse(
        string prompt,
        Guid roomId,
        [EnumeratorCancellation] CancellationToken ct = default)
    {
        var userId = Context.UserIdentifier ?? throw new HubException("User not authenticated");
        
        _logger.LogInformation("User {UserId} requested AI response in room {RoomId}", userId, roomId);

        // EN: Get conversation history for context
        // VI: Lấy lịch sử hội thoại cho context
        var history = await GetConversationHistoryAsync(roomId, ct);

        await foreach (var chunk in _aiService.StreamResponseAsync(prompt, history, ct))
        {
            yield return chunk;
        }
    }

    /// <summary>
    /// EN: Internal method to stream AI response to entire room.
    /// VI: Method nội bộ để stream AI response đến toàn bộ phòng.
    /// </summary>
    private async Task StreamAIResponseToRoom(Guid roomId, string prompt)
    {
        var aiMessageId = Guid.NewGuid();
        var fullResponse = new System.Text.StringBuilder();

        try
        {
            var history = await GetConversationHistoryAsync(roomId, CancellationToken.None);

            await foreach (var chunk in _aiService.StreamResponseAsync(prompt, history))
            {
                fullResponse.Append(chunk);
                await Clients.Group(roomId.ToString())
                    .ReceiveAIChunk(chunk, aiMessageId);
            }

            await Clients.Group(roomId.ToString())
                .AIResponseComplete(aiMessageId, fullResponse.ToString());

            _logger.LogInformation("AI response completed for room {RoomId}", roomId);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "AI streaming failed for room {RoomId}", roomId);
            await Clients.Group(roomId.ToString())
                .AIResponseComplete(aiMessageId, "[AI Error: Unable to generate response]");
        }
    }

    /// <summary>
    /// EN: Get conversation history for AI context.
    /// VI: Lấy lịch sử hội thoại cho AI context.
    /// </summary>
    private async Task<IEnumerable<ChatMessage>> GetConversationHistoryAsync(
        Guid roomId, 
        CancellationToken ct)
    {
        try
        {
            var conversation = await _conversationRepository.GetByIdAsync(roomId, ct);
            if (conversation == null)
            {
                return Enumerable.Empty<ChatMessage>();
            }

            return conversation.Messages
                .OrderByDescending(m => m.CreatedAt)
                .Take(20)
                .Reverse()
                .Select(m => new ChatMessage(
                    Role: m.SenderId.ToString() == Context.UserIdentifier ? "user" : "assistant",
                    Content: m.EncryptedContent, // EN: Note: Encrypted content / VI: Lưu ý: Nội dung đã mã hóa
                    Timestamp: m.CreatedAt
                ));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to get conversation history for room {RoomId}", roomId);
            return Enumerable.Empty<ChatMessage>();
        }
    }

    #endregion
}
