<?php

declare(strict_types=1);

namespace PhpWA\Media;

use PhpWA\Exception\ProtocolException;
use PhpWA\Runtime\Deadline;

final class MediaUploader
{
    /** @var MediaCrypto */ private $crypto;
    /** @var MediaConnectionDirectory */ private $connections;
    /** @var MediaHttpClientInterface */ private $http;
    public function __construct(MediaCrypto $crypto, MediaConnectionDirectory $connections, ?MediaHttpClientInterface $http = null)
    {
        $this->crypto = $crypto; $this->connections = $connections; $this->http = $http ?? new CurlMediaHttpClient();
    }
    public function uploadFile(string $inputPath, string $encryptedPath, string $type, Deadline $deadline): MediaUploadResult
    {
        $encrypted = $this->crypto->encryptFile($inputPath, $encryptedPath, $type, $deadline);
        $connection = $this->connections->get($deadline);
        $token = self::base64Url($encrypted->fileEncSha256());
        $query = http_build_query(['auth' => $connection->auth(), 'token' => $token], '', '&', PHP_QUERY_RFC3986);
        $lastError = null;
        foreach ($connection->hosts() as $host) {
            $url = 'https://' . $host . '/mms/' . MediaType::mmsPath($type) . '/' . rawurlencode($token) . '?' . $query;
            try {
                $body = $this->http->upload($url, $encrypted->path(), $encrypted->encryptedLength(), $deadline);
                return $this->parse($encrypted, $body);
            } catch (\Throwable $error) { $lastError = $error; if ($deadline->isExpired()) { break; } }
        }
        throw new ProtocolException('All WhatsApp media upload hosts failed.', 0, $lastError);
    }
    private function parse(EncryptedMedia $encrypted, string $body): MediaUploadResult
    {
        if ($body === '' || strlen($body) > 1048576) { throw new ProtocolException('Media upload response is empty or too large.'); }
        try { $data = json_decode($body, true, 16, JSON_THROW_ON_ERROR); }
        catch (\Throwable $error) { throw new ProtocolException('Media upload response is malformed.', 0, $error); }
        if (!is_array($data) || !is_string($data['url'] ?? null) || !is_string($data['direct_path'] ?? null)) {
            throw new ProtocolException('Media upload response is missing required fields.');
        }
        try { return new MediaUploadResult($encrypted, $data['url'], $data['direct_path']); }
        catch (\Throwable $error) { throw new ProtocolException('Media upload response fields are invalid.', 0, $error); }
    }
    private static function base64Url(string $bytes): string { return strtr(base64_encode($bytes), '+/', '-_'); }
}
