package handlers

import (
	"context"
	//	"encoding/base64"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gitlab.com/patrickbritton3/sojorn/go-backend/internal/realtime"
	"gitlab.com/patrickbritton3/sojorn/go-backend/internal/repository"
	"gitlab.com/patrickbritton3/sojorn/go-backend/internal/services"
	"github.com/rs/zerolog/log"
)

type ChatHandler struct {
	chatRepo            *repository.ChatRepository
	notificationService *services.NotificationService
	hub                 *realtime.Hub
}

func NewChatHandler(chatRepo *repository.ChatRepository, notificationService *services.NotificationService, hub *realtime.Hub) *ChatHandler {
	return &ChatHandler{
		chatRepo:            chatRepo,
		notificationService: notificationService,
		hub:                 hub,
	}
}

func (h *ChatHandler) GetConversations(c *gin.Context) {
	userIDStr, _ := c.Get("user_id")
	conversations, err := h.chatRepo.GetConversations(c.Request.Context(), userIDStr.(string))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch conversations"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"conversations": conversations})
}

func (h *ChatHandler) GetOrCreateConversation(c *gin.Context) {
	userIDStr, _ := c.Get("user_id")
	otherUserID := c.Query("other_user_id")
	if otherUserID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "other_user_id is required"})
		return
	}

	id, err := h.chatRepo.GetOrCreateConversation(c.Request.Context(), userIDStr.(string), otherUserID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to handle conversation"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"conversation_id": id})
}

func (h *ChatHandler) SendMessage(c *gin.Context) {
	senderIDStr, _ := c.Get("user_id")
	senderID, err := uuid.Parse(senderIDStr.(string))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid sender ID"})
		return
	}

	var req struct {
		ConversationID string `json:"conversation_id" binding:"required"`
		ReceiverID     string `json:"receiver_id"`
		Ciphertext     string `json:"ciphertext" binding:"required"`
		IV             string `json:"iv"`
		KeyVersion     string `json:"key_version"`
		MessageHeader  string `json:"message_header"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	convID, err := uuid.Parse(req.ConversationID)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid conversation ID"})
		return
	}

	pA, pB, err := h.chatRepo.GetParticipants(c.Request.Context(), req.ConversationID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to authenticate conversation participants"})
		return
	}

	if senderID.String() != pA && senderID.String() != pB {
		c.JSON(http.StatusForbidden, gin.H{"error": "You are not a participant in this conversation"})
		return
	}

	otherParticipant := pA
	if senderID.String() == pA {
		otherParticipant = pB
	}

	var receiverID uuid.UUID
	if req.ReceiverID != "" {
		receiverID, err = uuid.Parse(req.ReceiverID)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid receiver ID"})
			return
		}

		if receiverID.String() != otherParticipant {
			c.JSON(http.StatusForbidden, gin.H{"error": "Receiver is not a participant in this conversation"})
			return
		}
	} else {
		receiverID, _ = uuid.Parse(otherParticipant)
	}

	// Persist blind ciphertext to DB
	msg, err := h.chatRepo.CreateMessage(c.Request.Context(), senderID, receiverID, convID, req.Ciphertext, req.IV, req.KeyVersion, req.MessageHeader)
	if err != nil {
		log.Error().Err(err).Msg("Failed to persist secure message")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send message"})
		return
	}

	// Prepare Real-time Payload
	rtPayload := gin.H{
		"type": "new_message",
		"payload": gin.H{
			"id":              msg.ID,
			"conversation_id": msg.ConversationID,
			"sender_id":       msg.SenderID,
			"receiver_id":     msg.ReceiverID,
			"ciphertext":      msg.Ciphertext,
			"iv":              msg.IV,
			"key_version":     msg.KeyVersion, // e.g. "x3dh"
			"message_header":  msg.MessageHeader,
			"created_at":      msg.CreatedAt,
		},
	}

	// 1. Send via WebSocket (Best Effort, Immediate)
	h.hub.SendToUser(receiverID.String(), rtPayload)

	// 2. Send via Notification Service (Background, Reliable)
	if h.notificationService != nil {
		go func(recipID string, senderID string, convID string) {
			_ = h.notificationService.NotifyMessage(context.Background(), recipID, senderID, convID)
		}(receiverID.String(), senderID.String(), msg.ConversationID.String())
	}

	c.JSON(http.StatusCreated, msg)
}

func (h *ChatHandler) GetMessages(c *gin.Context) {
	convIDStr := c.Param("id")
	convID, err := uuid.Parse(convIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid conversation ID"})
		return
	}

	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
	offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))

	messages, err := h.chatRepo.GetMessages(c.Request.Context(), convID, limit, offset)
	if err != nil {
		log.Error().Err(err).Str("conversation_id", convIDStr).Msg("Failed to fetch messages")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch messages"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"messages": messages})
}

func (h *ChatHandler) GetMutualFollows(c *gin.Context) {
	userIDStr, _ := c.Get("user_id")
	profiles, err := h.chatRepo.GetMutualFollows(c.Request.Context(), userIDStr.(string))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch mutual follows"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"profiles": profiles})
}

func (h *ChatHandler) DeleteConversation(c *gin.Context) {
	userIDStr, _ := c.Get("user_id")
	convIDStr := c.Param("id")
	convID, err := uuid.Parse(convIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid conversation ID"})
		return
	}

	err = h.chatRepo.DeleteConversation(c.Request.Context(), convID, userIDStr.(string))
	if err != nil {
		log.Error().Err(err).Str("conversation_id", convIDStr).Msg("Failed to delete conversation")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete conversation"})
		return
	}

	// Broadcast deletion to current user via WebSocket
	deleteEvent := map[string]interface{}{
		"type": "conversation_deleted",
		"payload": map[string]interface{}{
			"conversation_id": convID.String(),
			"deleted_by":      userIDStr,
		},
	}

	// Send to current user (all their devices)
	_ = h.hub.SendToUser(userIDStr.(string), deleteEvent)

	c.JSON(http.StatusOK, gin.H{"success": true, "message": "Conversation permanently deleted"})
}

func (h *ChatHandler) DeleteMessage(c *gin.Context) {
	userIDStr, _ := c.Get("user_id")
	msgIDStr := c.Param("id")
	msgID, err := uuid.Parse(msgIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"})
		return
	}

	// Get conversation and recipient info before deleting
	var conversationID, senderID, receiverID uuid.UUID
	err = h.chatRepo.GetMessageInfo(c.Request.Context(), msgID, &conversationID, &senderID, &receiverID)
	if err != nil {
		log.Error().Err(err).Str("message_id", msgIDStr).Msg("Failed to get message info")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get message info"})
		return
	}

	err = h.chatRepo.DeleteMessage(c.Request.Context(), msgID, userIDStr.(string))
	if err != nil {
		log.Error().Err(err).Str("message_id", msgIDStr).Msg("Failed to delete message")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete message"})
		return
	}

	// Broadcast deletion to both participants via WebSocket
	deleteEvent := map[string]interface{}{
		"type": "message_deleted",
		"payload": map[string]interface{}{
			"message_id":      msgID.String(),
			"conversation_id": conversationID.String(),
			"deleted_by":      userIDStr,
		},
	}

	// Send to sender (all their devices)
	_ = h.hub.SendToUser(senderID.String(), deleteEvent)
	// Send to receiver (all their devices)
	_ = h.hub.SendToUser(receiverID.String(), deleteEvent)

	c.JSON(http.StatusOK, gin.H{"success": true, "message": "Message permanently deleted"})
}
