<?php

class Message
{
    private PDO $db;

    public function __construct()
    {
        $this->db = Database::getInstance();
    }

    public function create(array $data): int
    {
        $stmt = $this->db->prepare(
            'INSERT INTO messages (sender_id, recipient_id, listing_id, subject, body)
             VALUES (:sender_id, :recipient_id, :listing_id, :subject, :body)'
        );
        $stmt->execute([
            'sender_id'    => $data['sender_id'],
            'recipient_id' => $data['recipient_id'],
            'listing_id'   => $data['listing_id'] ?: null,
            'subject'      => $data['subject'] ?? null,
            'body'         => $data['body'],
        ]);

        return (int)$this->db->lastInsertId();
    }

    public function getConversationList(int $userId): array
    {
        $stmt = $this->db->prepare(
            'SELECT m.*,
                    sender.username AS sender_username,
                    sender.display_name AS sender_display_name,
                    sender.avatar AS sender_avatar,
                    recipient.username AS recipient_username,
                    recipient.display_name AS recipient_display_name,
                    l.title AS listing_title,
                    l.slug AS listing_slug,
                    l.status AS listing_status
             FROM messages m
             JOIN users sender ON m.sender_id = sender.id
             JOIN users recipient ON m.recipient_id = recipient.id
             LEFT JOIN listings l ON m.listing_id = l.id
             WHERE m.sender_id = :sender_user_id OR m.recipient_id = :recipient_user_id
             ORDER BY m.created_at DESC, m.id DESC'
        );
        $stmt->execute(['sender_user_id' => $userId, 'recipient_user_id' => $userId]);

        return $stmt->fetchAll();
    }

    public function findForUser(int $id, int $userId): ?array
    {
        $stmt = $this->db->prepare(
            'SELECT m.*,
                    sender.username AS sender_username,
                    sender.display_name AS sender_display_name,
                    sender.avatar AS sender_avatar,
                    recipient.username AS recipient_username,
                    recipient.display_name AS recipient_display_name,
                    recipient.avatar AS recipient_avatar,
                    l.title AS listing_title,
                    l.slug AS listing_slug
             FROM messages m
             JOIN users sender ON m.sender_id = sender.id
             JOIN users recipient ON m.recipient_id = recipient.id
             LEFT JOIN listings l ON m.listing_id = l.id
             WHERE m.id = :id AND (m.sender_id = :sender_user_id OR m.recipient_id = :recipient_user_id)
             LIMIT 1'
        );
        $stmt->execute(['id' => $id, 'sender_user_id' => $userId, 'recipient_user_id' => $userId]);
        $message = $stmt->fetch();

        return $message ?: null;
    }

    public function markRead(int $id, int $recipientId): void
    {
        $stmt = $this->db->prepare(
            'UPDATE messages SET is_read = 1 WHERE id = :id AND recipient_id = :recipient_id'
        );
        $stmt->execute(['id' => $id, 'recipient_id' => $recipientId]);
    }

    public function countUnreadForUser(int $userId): int
    {
        $stmt = $this->db->prepare(
            'SELECT COUNT(*) FROM messages WHERE recipient_id = :user_id AND is_read = 0'
        );
        $stmt->execute(['user_id' => $userId]);

        return (int)$stmt->fetchColumn();
    }

    public function getConversationBetweenUsers(int $userId1, int $userId2): array
    {
        $stmt = $this->db->prepare(
            'SELECT m.*,
                    sender.username AS sender_username,
                    sender.display_name AS sender_display_name,
                    sender.avatar AS sender_avatar,
                    recipient.username AS recipient_username,
                    recipient.display_name AS recipient_display_name,
                    recipient.avatar AS recipient_avatar
             FROM messages m
             JOIN users sender ON m.sender_id = sender.id
             JOIN users recipient ON m.recipient_id = recipient.id
             WHERE (m.sender_id = :user1_id AND m.recipient_id = :user2_id)
                OR (m.sender_id = :user2_id AND m.recipient_id = :user1_id)
             ORDER BY m.created_at ASC, m.id ASC'
        );
        $stmt->execute(['user1_id' => $userId1, 'user2_id' => $userId2]);
        return $stmt->fetchAll();
    }

    public function getConversationForListing(int $userId, int $listingId): array
    {
        $stmt = $this->db->prepare(
            'SELECT m.*,
                    sender.username AS sender_username,
                    sender.display_name AS sender_display_name,
                    sender.avatar AS sender_avatar,
                    recipient.username AS recipient_username,
                    recipient.display_name AS recipient_display_name,
                    recipient.avatar AS recipient_avatar,
                    l.title AS listing_title,
                    l.slug AS listing_slug
             FROM messages m
             JOIN users sender ON m.sender_id = sender.id
             JOIN users recipient ON m.recipient_id = recipient.id
             LEFT JOIN listings l ON m.listing_id = l.id
             WHERE m.listing_id = :listing_id
               AND (m.sender_id = :sender_user_id OR m.recipient_id = :recipient_user_id)
             ORDER BY m.created_at ASC, m.id ASC'
        );
        $stmt->execute(['listing_id' => $listingId, 'sender_user_id' => $userId, 'recipient_user_id' => $userId]);
        return $stmt->fetchAll();
    }

    public function countByUser(int $userId): int
    {
        $stmt = $this->db->prepare('SELECT COUNT(*) FROM saved_searches WHERE user_id = :user_id');
        $stmt->execute(['user_id' => $userId]);
        return (int)$stmt->fetchColumn();
    }

    public function deleteForUser(int $messageId, int $userId): bool
    {
        // Users can only delete messages they sent or received
        $stmt = $this->db->prepare(
            'DELETE FROM messages WHERE id = :id AND (sender_id = :sender_user_id OR recipient_id = :recipient_user_id)'
        );
        return $stmt->execute(['id' => $messageId, 'sender_user_id' => $userId, 'recipient_user_id' => $userId]) && $stmt->rowCount() > 0;
    }

    /**
     * Return distinct users who exchanged messages with $sellerId about $listingId.
     * Used by the seller when picking the buyer at "mark as sold" time.
     */
    public function getCounterpartiesForListing(int $listingId, int $sellerId): array
    {
        $stmt = $this->db->prepare(
            'SELECT u.id, u.username, u.display_name, u.avatar, COUNT(m.id) AS message_count, MAX(m.created_at) AS last_message_at
             FROM messages m
             JOIN users u ON u.id = CASE WHEN m.sender_id = :seller_id THEN m.recipient_id ELSE m.sender_id END
             WHERE m.listing_id = :listing_id
               AND (m.sender_id = :seller_id_2 OR m.recipient_id = :seller_id_3)
               AND u.id <> :seller_id_4
             GROUP BY u.id, u.username, u.display_name, u.avatar
             ORDER BY last_message_at DESC'
        );
        $stmt->execute([
            'listing_id'  => $listingId,
            'seller_id'   => $sellerId,
            'seller_id_2' => $sellerId,
            'seller_id_3' => $sellerId,
            'seller_id_4' => $sellerId,
        ]);
        return $stmt->fetchAll();
    }
}
