<?php

declare(strict_types=1);

namespace PhpWA\Client;

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

/** Resolves a bounded, TLS-only WhatsApp profile-picture URL. */
final class ProfilePictureDirectory
{
    /** @var QueryExecutorInterface */ private $queries;
    /** @var MessageIdGeneratorInterface */ private $ids;

    public function __construct(QueryExecutorInterface $queries, ?MessageIdGeneratorInterface $ids = null)
    {
        $this->queries = $queries;
        $this->ids = $ids ?? new RandomMessageIdGenerator();
    }

    public function url(string $jidText, Deadline $deadline, bool $preview = true): ?string
    {
        $jid = Jid::parse($jidText);
        if (!$jid instanceof Jid || $jid->device() !== null
            || (!$jid->isPhoneNumber() && !$jid->isHostedPhoneNumber() && !$jid->isLid()
                && !$jid->isHostedLid() && !$jid->isGroup())) {
            throw new InvalidArgumentException('Profile picture requires a base user or group JID.');
        }

        $response = $this->queries->query(BinaryNode::children('iq', [
            'to' => ServerJid::WHATSAPP,
            'target' => (string) $jid,
            'type' => 'get',
            'xmlns' => 'w:profile:picture',
            'id' => $this->ids->generate(),
        ], [new BinaryNode('picture', ['type' => $preview ? 'preview' : 'image', 'query' => 'url'])]), $deadline);

        $picture = $response->child('picture');
        if (!$picture instanceof BinaryNode) {
            return null;
        }
        $url = $picture->attribute('url');
        if ($url === null || $url === '') {
            return null;
        }
        $this->assertSafeUrl($url);
        return $url;
    }

    private function assertSafeUrl(string $url): void
    {
        if (strlen($url) > 2048 || preg_match('//u', $url) !== 1 || preg_match('/[\x00-\x20\x7f]/', $url) === 1) {
            throw new ProtocolException('WhatsApp profile picture URL is malformed.');
        }
        $parts = parse_url($url);
        $host = is_array($parts) && isset($parts['host']) && is_string($parts['host'])
            ? strtolower(rtrim($parts['host'], '.'))
            : '';
        if (!is_array($parts) || ($parts['scheme'] ?? null) !== 'https' || $host === ''
            || isset($parts['user']) || isset($parts['pass']) || isset($parts['fragment'])
            || (isset($parts['port']) && (int) $parts['port'] !== 443)
            || ($host !== 'whatsapp.net' && substr($host, -13) !== '.whatsapp.net')) {
            throw new ProtocolException('WhatsApp profile picture URL violates the trusted-host policy.');
        }
    }
}
