<?php

declare(strict_types=1);

namespace PhpWA\Client;

use InvalidArgumentException;
use PhpWA\Protocol\Binary\BinaryNode;
use PhpWA\Protocol\Jid\Jid;
use PhpWA\Runtime\Deadline;

final class ReadReceiptSender
{
    /** @var ProtocolConnectionInterface */ private $connection;
    public function __construct(ProtocolConnectionInterface $connection) { $this->connection = $connection; }

    /** @param list<string> $messageIds */
    public function markRead(array $messageIds, string $chatJid, ?string $participantJid, int $timestamp, Deadline $deadline): void
    {
        $this->send($messageIds, $chatJid, $participantJid, $timestamp, 'read', $deadline);
    }

    /** @param list<string> $messageIds */
    public function markPlayed(array $messageIds, string $chatJid, ?string $participantJid, int $timestamp, Deadline $deadline): void
    {
        $this->send($messageIds, $chatJid, $participantJid, $timestamp, 'played', $deadline);
    }

    /** @param list<string> $messageIds */
    private function send(array $messageIds, string $chatJid, ?string $participantJid, int $timestamp, string $type, Deadline $deadline): void
    {
        if ($messageIds === [] || count($messageIds) > 1000 || $timestamp < 0) {
            throw new InvalidArgumentException('Read receipt message list or timestamp is invalid.');
        }
        $chat = Jid::parse($chatJid);
        if (!$chat instanceof Jid) { throw new InvalidArgumentException('Read receipt chat JID is invalid.'); }
        $ids = [];
        foreach ($messageIds as $id) {
            if (!is_string($id) || preg_match('/\A[A-Za-z0-9._:-]{1,256}\z/D', $id) !== 1 || isset($ids[$id])) {
                throw new InvalidArgumentException('Read receipt contains an invalid or duplicate message ID.');
            }
            $ids[$id] = true;
        }
        $attrs = ['id' => $messageIds[0], 'type' => $type, 'to' => (string) $chat, 't' => (string) $timestamp];
        if ($chat->isGroup()) {
            $participant = Jid::parse($participantJid);
            if (!$participant instanceof Jid || $participant->isGroup() || $participant->isBroadcast()) {
                throw new InvalidArgumentException('Group read receipt requires a participant JID.');
            }
            $attrs['participant'] = (string) new Jid($participant->user(), $participant->server(), null, $participant->domainType());
        } elseif ($participantJid !== null) {
            throw new InvalidArgumentException('Direct read receipt must not contain a participant.');
        }
        $children = [];
        if (count($messageIds) > 1) {
            $items = [];
            foreach (array_slice($messageIds, 1) as $id) { $items[] = new BinaryNode('item', ['id' => $id]); }
            $children[] = BinaryNode::children('list', [], $items);
        }
        $node = $children === [] ? new BinaryNode('receipt', $attrs) : BinaryNode::children('receipt', $attrs, $children);
        $this->connection->send($node, $deadline);
    }
}
