<?php

declare(strict_types=1);

namespace PhpWA\Client;

use PhpWA\Exception\ProtocolException;
use PhpWA\Protocol\Binary\BinaryNode;
use PhpWA\Protocol\Jid\ServerJid;
use PhpWA\Runtime\Deadline;

/** Deterministic online stanza dispatch with security-sensitive priority. */
final class StanzaRouter
{
    /** @var ProtocolConnectionInterface */ private $connection;
    /** @var IncomingMessageProcessor */ private $messages;
    /** @var OutgoingRetryReceiptProcessor */ private $retries;
    /** @var ReceiptProcessor */ private $receipts;
    /** @var OutgoingAckProcessor */ private $acks;
    /** @var CallProcessor */ private $calls;
    /** @var NotificationProcessor */ private $notifications;

    public function __construct(
        ProtocolConnectionInterface $connection,
        IncomingMessageProcessor $messages,
        OutgoingRetryReceiptProcessor $retries,
        ReceiptProcessor $receipts,
        OutgoingAckProcessor $acks,
        CallProcessor $calls,
        NotificationProcessor $notifications
    ) {
        $this->connection = $connection; $this->messages = $messages; $this->retries = $retries;
        $this->receipts = $receipts; $this->acks = $acks; $this->calls = $calls; $this->notifications = $notifications;
    }

    public function route(BinaryNode $node, Deadline $deadline): string
    {
        if ($this->acks->process($node)) { return 'outgoing_ack'; }
        if ($this->retries->process($node, $deadline)) { return 'outgoing_retry'; }
        if ($this->receipts->process($node, $deadline)) { return 'receipt'; }
        if ($node->tag() === 'message') { $this->messages->process($node, $deadline); return 'message'; }
        if ($this->calls->process($node, $deadline)) { return 'call'; }
        if ($this->notifications->process($node, $deadline)) { return 'notification'; }
        if ($node->tag() === 'iq' && $node->attribute('type') === 'get' && $node->attribute('xmlns') === 'urn:xmpp:ping') {
            $id = $node->attribute('id');
            if ($id === null || $id === '' || strlen($id) > 256 || preg_match('/[\x00-\x1f\x7f]/', $id) === 1) {
                throw new ProtocolException('Incoming ping IQ has an invalid ID.');
            }
            $this->connection->send(new BinaryNode('iq', ['to' => ServerJid::WHATSAPP, 'type' => 'result', 'id' => $id]), $deadline);
            return 'ping';
        }
        return 'ignored';
    }
}
