<?php

declare (strict_types=1);
namespace OCA\Notifications\Vendor\SpomkyLabs\Pki\CryptoEncoding;

use ArrayIterator;
use Countable;
use IteratorAggregate;
use LogicException;
use RuntimeException;
use Stringable;
use UnexpectedValueException;
use function count;
use function is_string;
use const PREG_SET_ORDER;
/**
 * Container for multiple PEM objects.
 *
 * The order of PEMs shall be retained, eg. when read from a file.
 */
final class PEMBundle implements Countable, IteratorAggregate, Stringable
{
    /**
     * Array of PEM objects.
     *
     * @var PEM[]
     */
    private array $pems;
    private function __construct(PEM ...$pems)
    {
        $this->pems = $pems;
    }
    public function __toString(): string
    {
        return $this->string();
    }
    public static function create(PEM ...$pems): self
    {
        return new self(...$pems);
    }
    /**
     * Initialize from a string.
     */
    public static function fromString(string $str): self
    {
        $hasMatches = preg_match_all(PEM::PEM_REGEX, $str, $matches, PREG_SET_ORDER);
        if ($hasMatches === false || $hasMatches === 0) {
            throw new UnexpectedValueException('No PEM blocks.');
        }
        $pems = array_map(static function ($match) {
            $payload = preg_replace('/\s+/', '', $match[2]);
            if (!is_string($payload)) {
                throw new UnexpectedValueException('Failed to decode PEM data.');
            }
            $data = base64_decode($payload, true);
            if ($data === false) {
                throw new UnexpectedValueException('Failed to decode PEM data.');
            }
            return PEM::create($match[1], $data);
        }, $matches);
        return self::create(...$pems);
    }
    /**
     * Initialize from a file.
     */
    public static function fromFile(string $filename): self
    {
        if (!is_readable($filename)) {
            throw new RuntimeException("Failed to read {$filename}.");
        }
        $str = file_get_contents($filename);
        if ($str === false) {
            throw new RuntimeException("Failed to read {$filename}.");
        }
        return self::fromString($str);
    }
    /**
     * Get self with PEM objects appended.
     */
    public function withPEMs(PEM ...$pems): self
    {
        $obj = clone $this;
        $obj->pems = array_merge($obj->pems, $pems);
        return $obj;
    }
    /**
     * Get all PEMs in a bundle.
     *
     * @return PEM[]
     */
    public function all(): array
    {
        return $this->pems;
    }
    /**
     * Get the first PEM in a bundle.
     */
    public function first(): PEM
    {
        if (count($this->pems) === 0) {
            throw new LogicException('No PEMs.');
        }
        return $this->pems[0];
    }
    /**
     * Get the last PEM in a bundle.
     */
    public function last(): PEM
    {
        if (count($this->pems) === 0) {
            throw new LogicException('No PEMs.');
        }
        return $this->pems[count($this->pems) - 1];
    }
    /**
     * @see \Countable::count()
     */
    public function count(): int
    {
        return count($this->pems);
    }
    /**
     * Get iterator for PEMs.
     *
     * @see \IteratorAggregate::getIterator()
     */
    public function getIterator(): ArrayIterator
    {
        return new ArrayIterator($this->pems);
    }
    /**
     * Encode bundle to a string of contiguous PEM blocks.
     */
    public function string(): string
    {
        return implode("\n", array_map(static fn(PEM $pem) => $pem->string(), $this->pems));
    }
}