<?php

declare(strict_types=1);

namespace NoahVet\Reef\Plugin\Cache\Model;

use Http\Discovery\Psr17FactoryDiscovery;
use Psr\Http\Message\StreamInterface;

class CacheableResponse implements \Serializable
{
    /**
     * @var array<string, list<string>>
     */
    private array $headers;

    /**
     * @param array<string, array<string>> $headers
     */
    public function __construct(
        private string $protocol,
        private int $statusCode,
        private string $reasonPhrase,
        array $headers,
        private StreamInterface $body,
    ) {
        $this->headers = [];
        foreach ($headers as $name => $values) {
            $lowercaseName = \strtr($name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
            $this->headers[$lowercaseName] = \array_values($values);
        }
        $this->body->rewind();
    }

    /**
     * @return array{
     *     protocol: string,
     *     statusCode: int,
     *     reasonPhrase: string,
     *     headers: array<string, list<string>>,
     *     body: string,
     * }
     */
    public function __serialize(): array
    {
        $this->body->rewind();

        $ret = [
            'protocol' => $this->protocol,
            'statusCode' => $this->statusCode,
            'reasonPhrase' => $this->reasonPhrase,
            'headers' => $this->headers,
            'body' => $this->body->getContents(),
        ];

        $this->body->rewind();

        return $ret;
    }

    /**
     * @param array{
     *      protocol: string,
     *      statusCode: int,
     *      reasonPhrase: string,
     *      headers: array<string, list<string>>,
     *      body: string,
     *  } $data
     */
    public function __unserialize(array $data): void
    {
        $stream = \fopen('php://memory', 'w+');
        \assert(\is_resource($stream));
        \fwrite($stream, $data['body']);
        \rewind($stream);

        $this->protocol = $data['protocol'];
        $this->statusCode = $data['statusCode'];
        $this->reasonPhrase = $data['reasonPhrase'];
        $this->headers = $data['headers'];
        $this->body = Psr17FactoryDiscovery::findStreamFactory()->createStreamFromResource($stream);
    }

    public function getProtocolVersion(): string
    {
        return $this->protocol;
    }

    /**
     * @return array<string, list<string>>
     */
    public function getHeaders(): array
    {
        return $this->headers;
    }

    public function getBody(): StreamInterface
    {
        return $this->body;
    }

    public function getStatusCode(): int
    {
        return $this->statusCode;
    }

    public function getReasonPhrase(): string
    {
        return $this->reasonPhrase;
    }

    public function serialize(): string
    {
        return \serialize($this->__serialize());
    }

    public function unserialize(string $data): void
    {
        $this->unserialize(\unserialize($data));
    }
}
