<?php

declare(strict_types=1);

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2021 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace Webauthn\MetadataService;

use Assert\Assertion;
use Base64Url\Base64Url;
use function count;
use JsonSerializable;
use LogicException;

class MetadataTOCPayloadEntry implements JsonSerializable
{
    /**
     * @var string|null
     */
    private $aaid;

    /**
     * @var string|null
     */
    private $aaguid;

    /**
     * @var string[]
     */
    private $attestationCertificateKeyIdentifiers = [];

    /**
     * @var string|null
     */
    private $hash;

    /**
     * @var string|null
     */
    private $url;

    /**
     * @var StatusReport[]
     */
    private $statusReports = [];

    /**
     * @var string
     */
    private $timeOfLastStatusChange;

    /**
     * @var string
     */
    private $rogueListURL;

    /**
     * @var string
     */
    private $rogueListHash;

    public function __construct(?string $aaid, ?string $aaguid, array $attestationCertificateKeyIdentifiers, ?string $hash, ?string $url, string $timeOfLastStatusChange, ?string $rogueListURL, ?string $rogueListHash)
    {
        if (null !== $aaid && null !== $aaguid) {
            throw new LogicException('Authenticators cannot support both AAID and AAGUID');
        }
        if (null === $aaid && null === $aaguid && 0 === count($attestationCertificateKeyIdentifiers)) {
            throw new LogicException('If neither AAID nor AAGUID are set, the attestation certificate identifier list shall not be empty');
        }
        foreach ($attestationCertificateKeyIdentifiers as $attestationCertificateKeyIdentifier) {
            Assertion::string($attestationCertificateKeyIdentifier, Utils::logicException('Invalid attestation certificate identifier. Shall be a list of strings'));
            Assertion::notEmpty($attestationCertificateKeyIdentifier, Utils::logicException('Invalid attestation certificate identifier. Shall be a list of strings'));
            Assertion::regex($attestationCertificateKeyIdentifier, '/^[0-9a-f]+$/', Utils::logicException('Invalid attestation certificate identifier. Shall be a list of strings'));
        }
        $this->aaid = $aaid;
        $this->aaguid = $aaguid;
        $this->attestationCertificateKeyIdentifiers = $attestationCertificateKeyIdentifiers;
        $this->hash = Base64Url::decode($hash);
        $this->url = $url;
        $this->timeOfLastStatusChange = $timeOfLastStatusChange;
        $this->rogueListURL = $rogueListURL;
        $this->rogueListHash = $rogueListHash;
    }

    public function getAaid(): ?string
    {
        return $this->aaid;
    }

    public function getAaguid(): ?string
    {
        return $this->aaguid;
    }

    public function getAttestationCertificateKeyIdentifiers(): array
    {
        return $this->attestationCertificateKeyIdentifiers;
    }

    public function getHash(): ?string
    {
        return $this->hash;
    }

    public function getUrl(): ?string
    {
        return $this->url;
    }

    public function addStatusReports(StatusReport $statusReport): self
    {
        $this->statusReports[] = $statusReport;

        return $this;
    }

    /**
     * @return StatusReport[]
     */
    public function getStatusReports(): array
    {
        return $this->statusReports;
    }

    public function getTimeOfLastStatusChange(): string
    {
        return $this->timeOfLastStatusChange;
    }

    public function getRogueListURL(): string
    {
        return $this->rogueListURL;
    }

    public function getRogueListHash(): string
    {
        return $this->rogueListHash;
    }

    public static function createFromArray(array $data): self
    {
        $data = Utils::filterNullValues($data);
        Assertion::keyExists($data, 'timeOfLastStatusChange', Utils::logicException('Invalid data. The parameter "timeOfLastStatusChange" is missing'));
        Assertion::keyExists($data, 'statusReports', Utils::logicException('Invalid data. The parameter "statusReports" is missing'));
        Assertion::isArray($data['statusReports'], Utils::logicException('Invalid data. The parameter "statusReports" shall be an array of StatusReport objects'));
        $object = new self(
        $data['aaid'] ?? null,
        $data['aaguid'] ?? null,
        $data['attestationCertificateKeyIdentifiers'] ?? [],
        $data['hash'] ?? null,
        $data['url'] ?? null,
            $data['timeOfLastStatusChange'],
            $data['rogueListURL'] ?? null,
            $data['rogueListHash'] ?? null
        );
        foreach ($data['statusReports'] as $statusReport) {
            $object->addStatusReports(StatusReport::createFromArray($statusReport));
        }

        return $object;
    }

    public function jsonSerialize(): array
    {
        $data = [
            'aaid' => $this->aaid,
            'aaguid' => $this->aaguid,
            'attestationCertificateKeyIdentifiers' => $this->attestationCertificateKeyIdentifiers,
            'hash' => Base64Url::encode($this->hash),
            'url' => $this->url,
            'statusReports' => array_map(static function (StatusReport $object): array {
                return $object->jsonSerialize();
            }, $this->statusReports),
            'timeOfLastStatusChange' => $this->timeOfLastStatusChange,
            'rogueListURL' => $this->rogueListURL,
            'rogueListHash' => $this->rogueListHash,
        ];

        return Utils::filterNullValues($data);
    }
}