<?php

namespace Velis\Http;

use Velis\Filesystem\FilesystemInterface;
use Velis\Http\Request;
use Velis\Http\Response;

class Stream
{
    private Request $request;
    private Response $response;

    public function __construct(Request $request, Response $response)
    {
        $this->request = $request;
        $this->response = $response;
    }


    /**
     * @return void
     */
    public function contentFromFile(string $storagePath, FilesystemInterface $filesystem): void
    {
        $range = $this->getRange();

        if (is_array($range) && $range[1]) {
            $length = $range[1] - $range[0] + 1;
            $size = $filesystem->size($storagePath);
            $this->setContentHeaders($range, $size);

            $stream = $filesystem->createStream($storagePath);
            $stream->open();
            $stream->seek($range[0], SEEK_SET);
            $data = $stream->read($length);
            $stream->close();

            $this->response->setContent($data);

            $this->setContentLengthHeader($length);
        } else {
            // Disable output buffering - buffer cleaning and disable
            // (required for large files)
            while (ob_end_clean()) {
                // do nothing - just clear buffer
            }
            $this->setContentLengthHeader($filesystem->size($storagePath));
            $this->setContentTypeHeader('application/octet-stream');
            $this->response->setContent($filesystem->read($storagePath));
        }
    }

    /**
     * @param resource $resource
     * @return void
     */
    public function contentFromResource($resource, string $fileName): void
    {
        $range = $this->getRange();
        $stats = fstat($resource);
        $size = $stats['size'];

        if (is_array($range) && $range[1]) {
            $length = $range[1] - $range[0] + 1;

            $this->setContentHeaders($range, $size);

            fseek($resource, $range[0], SEEK_SET);
            $data = fread($resource, $length);

            $this->response->setContent($data);
            $this->setContentLengthHeader($length);
        } else {
            // Disable output buffering - buffer cleaning and disable
            // (required for large files)
            while (ob_end_clean()) {
                // do nothing - just clear buffer
            }
            header('content-type: application/octet-stream');
            header('content-disposition: attachment; filename="' . $fileName . '"');
            header('content-length: ' . $size);

            fpassthru($resource);
        }
    }

    private function getRange(): ?array
    {
        if ($range = $this->request->getHeader('Range')) {
            return explode('-', str_replace('bytes=', '', $range));
        }
        return null;
    }
    /**
     * @param array<int,mixed> $range
     */
    private function setContentHeaders(array $range, int $size): void
    {
        $this->response->setStatusCode(206, "Partial Content");
        $this->response->setHeader('Accept-Ranges', 'bytes');
        $this->response->setHeader('Content-Range', "bytes " . $range[0] . '-' . $range[1] . '/' . $size);
    }

    private function setContentTypeHeader(string $contentType): void
    {
        $this->response->setHeader('Content-Type', $contentType);
    }

    private function setContentLengthHeader(int $length): void
    {
        $this->response->setHeader('Content-Length', $length);
    }
}
