// internal/repository/chat_repository.go
package repository

import (
	"context"
	"database/sql"
	"fmt"
	"websocket-server/internal/model"

	"github.com/google/uuid"
)

type ChatRepository struct {
	db *sql.DB
}

func NewChatRepository(db *sql.DB) *ChatRepository {
	return &ChatRepository{db: db}
}

// CreateOrGetRoom creates a chat room or returns existing one
func (r *ChatRepository) CreateOrGetRoom(ctx context.Context, customer1ID, customer2ID string) (*model.ChatRoom, error) {
	// Ensure customer1_id < customer2_id for consistency
	if customer1ID > customer2ID {
		customer1ID, customer2ID = customer2ID, customer1ID
	}

	var room model.ChatRoom
	query := `
		INSERT INTO chat_rooms (id, customer1_id, customer2_id, created_at, updated_at)
		VALUES ($1, $2, $3, NOW(), NOW())
		ON CONFLICT (customer1_id, customer2_id)
		DO UPDATE SET updated_at = NOW()
		RETURNING id, customer1_id, customer2_id, last_message_at, created_at
	`

	roomID := uuid.New().String()
	err := r.db.QueryRowContext(ctx, query, roomID, customer1ID, customer2ID).Scan(
		&room.ID, &room.Customer1ID, &room.Customer2ID, &room.LastMessageAt, &room.CreatedAt,
	)
	if err != nil {
		return nil, fmt.Errorf("failed to create/get room: %w", err)
	}

	return &room, nil
}

// SaveMessage saves a chat message
func (r *ChatRepository) SaveMessage(ctx context.Context, msg *model.ChatMessage) error {
	query := `
        INSERT INTO chat_messages (id, room_id, sender_id, receiver_id, content, reply_to_id, is_read, created_at, updated_at)
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $8)
    `

	msg.ID = uuid.New().String()
	_, err := r.db.ExecContext(ctx, query,
		msg.ID, msg.RoomID, msg.SenderID, msg.ReceiverID, msg.Content, msg.ReplyToID, msg.IsRead, msg.CreatedAt,
	)

	if err != nil {
		return fmt.Errorf("failed to save message: %w", err)
	}

	go r.updateRoomLastMessage(context.Background(), msg)

	return nil
}

// GetMessageHistory retrieves chat history between two customers
func (r *ChatRepository) GetMessageHistory(ctx context.Context, customer1ID, customer2ID string, limit, offset int) ([]*model.ChatMessage, error) {
	query := `
        SELECT 
            m.id, m.room_id, m.sender_id, m.receiver_id, m.content, m.is_read, m.read_at, m.created_at, m.reply_to_id,
            rm.id        AS reply_id,
            rm.content   AS reply_content,
			COALESCE(c.full_name, 'کاربر') AS reply_sender_name
        FROM chat_messages m
        LEFT JOIN chat_messages rm ON rm.id = m.reply_to_id
		LEFT JOIN customers c ON c.id = rm.sender_id::uuid
        WHERE (m.sender_id = $1 AND m.receiver_id = $2) OR (m.sender_id = $2 AND m.receiver_id = $1)
        ORDER BY m.created_at DESC
        LIMIT $3 OFFSET $4
    `

	rows, err := r.db.QueryContext(ctx, query, customer1ID, customer2ID, limit, offset)
	if err != nil {
		return nil, fmt.Errorf("failed to get message history: %w", err)
	}
	defer rows.Close()

	var messages []*model.ChatMessage
	for rows.Next() {
		var msg model.ChatMessage
		var replyID, replyContent, replySenderName sql.NullString

		err := rows.Scan(
			&msg.ID, &msg.RoomID, &msg.SenderID, &msg.ReceiverID,
			&msg.Content, &msg.IsRead, &msg.ReadAt, &msg.CreatedAt, &msg.ReplyToID,
			&replyID, &replyContent, &replySenderName,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan message: %w", err)
		}

		if replyID.Valid {
			msg.ReplyTo = &model.ChatMessageReply{
				ID:         replyID.String,
				Content:    replyContent.String,
				SenderName: replySenderName.String,
			}
		}

		messages = append(messages, &msg)
	}

	return messages, nil
}

// MarkMessagesAsRead marks all messages from a customer as read
func (r *ChatRepository) MarkMessagesAsRead(ctx context.Context, receiverID, senderID string) error {
	query := `
		UPDATE chat_messages
		SET is_read = true, read_at = NOW()
		WHERE receiver_id = $1 AND sender_id = $2 AND is_read = false
	`
	_, err := r.db.ExecContext(ctx, query, receiverID, senderID)
	if err != nil {
		return fmt.Errorf("failed to mark messages as read: %w", err)
	}
	return nil
}

// GetUserRooms retrieves all chat rooms for a customer
func (r *ChatRepository) GetUserRooms(ctx context.Context, customerID string) ([]*model.ChatRoom, error) {
	query := `
		SELECT r.id, r.customer1_id, r.customer2_id, r.last_message_at, r.created_at,
		       (SELECT content FROM chat_messages
		        WHERE room_id = r.id
		        ORDER BY created_at DESC LIMIT 1) as last_message
		FROM chat_rooms r
		WHERE r.customer1_id = $1 OR r.customer2_id = $1
		ORDER BY COALESCE(r.last_message_at, r.created_at) DESC
	`

	rows, err := r.db.QueryContext(ctx, query, customerID)
	if err != nil {
		return nil, fmt.Errorf("failed to get user rooms: %w", err)
	}
	defer rows.Close()

	var rooms []*model.ChatRoom
	for rows.Next() {
		var room model.ChatRoom
		var lastMessage sql.NullString

		err := rows.Scan(&room.ID, &room.Customer1ID, &room.Customer2ID, &room.LastMessageAt, &room.CreatedAt, &lastMessage)
		if err != nil {
			return nil, fmt.Errorf("failed to scan room: %w", err)
		}

		if lastMessage.Valid {
			room.LastMessage = lastMessage.String
		}

		rooms = append(rooms, &room)
	}

	return rooms, nil
}

func (r *ChatRepository) updateRoomLastMessage(ctx context.Context, msg *model.ChatMessage) {
	query := `
		UPDATE chat_rooms
		SET last_message_at = $1, updated_at = NOW()
		WHERE id = $2
	`
	r.db.ExecContext(ctx, query, msg.CreatedAt, msg.RoomID)
}
