package websocket

import (
	"context"
	"encoding/json"
	"log"
	"time"

	"websocket-server/internal/model"

	"github.com/gorilla/websocket"
)

type ChatClient struct {
	Hub        *ChatHub
	Conn       *websocket.Conn
	Send       chan []byte
	CustomerID string
	Service    ChatService
}

type IncomingMessage struct {
	Type            string  `json:"type"`
	ReceiverID      string  `json:"receiver_id"`
	SenderID        string  `json:"sender_id"`
	Content         string  `json:"content"`
	OtherCustomerID string  `json:"other_customer_id"`
	ReplyToID       *string `json:"reply_to_id,omitempty"`
}

func (c *ChatClient) ReadPump() {
	defer func() {
		c.Hub.Unregister <- c
		c.Conn.Close()
	}()

	c.Conn.SetReadDeadline(time.Time{})
	c.Conn.SetPongHandler(func(string) error { return nil })

	for {
		_, rawMsg, err := c.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("ReadMessage error for customer %s: %v", c.CustomerID, err)
			}
			break
		}

		var msg IncomingMessage
		if err := json.Unmarshal(rawMsg, &msg); err != nil {
			log.Printf("Invalid message from customer %s: %v", c.CustomerID, err)
			continue
		}

		switch msg.Type {
		case "get_history":
			c.handleGetHistory(msg.OtherCustomerID)
		case "send_message":
			c.handleSendMessage(msg.ReceiverID, msg.Content, msg.ReplyToID)
		case "mark_read":
			c.handleMarkRead(msg.SenderID)
		case "typing":
			c.forwardToUser(msg.ReceiverID, map[string]string{
				"type":      "typing",
				"sender_id": c.CustomerID,
			})
		case "stop_typing":
			c.forwardToUser(msg.ReceiverID, map[string]string{
				"type":      "stop_typing",
				"sender_id": c.CustomerID,
			})
		default:
			log.Printf("Unknown message type from customer %s: %s", c.CustomerID, msg.Type)
		}
	}
}

func (c *ChatClient) handleGetHistory(otherCustomerID string) {
	messages, err := c.Service.GetMessageHistory(context.Background(), c.CustomerID, otherCustomerID, 50, 0)
	if err != nil {
		log.Printf("Error getting history: %v", err)
		c.sendJSON(map[string]interface{}{"type": "history", "messages": []interface{}{}})
		return
	}

	if messages == nil {
		messages = []*model.ChatMessage{}
	}

	c.sendJSON(map[string]interface{}{
		"type":     "history",
		"messages": messages,
	})
}
func (c *ChatClient) handleSendMessage(receiverID, content string, replyToID *string) {
	req := &model.ChatMessageRequest{
		ReceiverID: receiverID,
		Content:    content,
		ReplyToID:  replyToID,
	}

	savedMsg, err := c.Service.SendMessage(context.Background(), c.CustomerID, req)
	if err != nil {
		log.Printf("Error sending message: %v", err)
		return
	}

	payload := map[string]interface{}{
		"type":    "message",
		"message": savedMsg,
	}

	c.sendJSON(payload)
	c.forwardToUser(receiverID, payload)
}

func (c *ChatClient) handleMarkRead(senderID string) {
	err := c.Service.MarkAsRead(context.Background(), c.CustomerID, senderID)
	if err != nil {
		log.Printf("Error marking as read: %v", err)
		return
	}

	c.forwardToUser(senderID, map[string]string{
		"type":      "read",
		"reader_id": c.CustomerID,
	})
}

func (c *ChatClient) forwardToUser(targetID string, payload interface{}) {
	data, err := json.Marshal(payload)
	if err != nil {
		return
	}
	c.Hub.SendToUser(targetID, data)
}

func (c *ChatClient) sendJSON(payload interface{}) {
	data, err := json.Marshal(payload)
	if err != nil {
		return
	}
	select {
	case c.Send <- data:
	default:
		log.Printf("Send buffer full for customer %s", c.CustomerID)
	}
}

func (c *ChatClient) WritePump() {
	ticker := time.NewTicker(54 * time.Second)
	defer func() {
		ticker.Stop()
		c.Conn.Close()
	}()

	for {
		select {
		case message, ok := <-c.Send:
			c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
			if !ok {
				c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			if err := c.Conn.WriteMessage(websocket.TextMessage, message); err != nil {
				return
			}

		case <-ticker.C:
			c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
			if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}
}
