<?php

declare(strict_types=1);

namespace App\Framework\Cryptography;

use InvalidArgumentException;

/**
 * Digital Signature Result Value Object
 *
 * Represents the result of a digital signature operation including the signature
 * and all metadata needed for verification.
 */
final readonly class DigitalSignatureResult
{
    public function __construct(
        private string $signature,
        private string $algorithm,
        private string $hashAlgorithm,
        private int $keySize,
        private ?string $curve = null
    ) {
        if (empty($signature)) {
            throw new InvalidArgumentException('Signature cannot be empty');
        }

        if (empty($algorithm)) {
            throw new InvalidArgumentException('Algorithm cannot be empty');
        }

        if (empty($hashAlgorithm)) {
            throw new InvalidArgumentException('Hash algorithm cannot be empty');
        }

        $supportedAlgorithms = ['rsa', 'ecdsa'];
        if (! in_array($algorithm, $supportedAlgorithms, true)) {
            throw new InvalidArgumentException('Unsupported algorithm');
        }

        $supportedHashAlgorithms = ['sha256', 'sha384', 'sha512', 'sha224'];
        if (! in_array($hashAlgorithm, $supportedHashAlgorithms, true)) {
            throw new InvalidArgumentException('Unsupported hash algorithm');
        }

        if ($keySize < 256) {
            throw new InvalidArgumentException('Key size must be at least 256 bits');
        }
    }

    /**
     * Get the signature bytes
     */
    public function getSignature(): string
    {
        return $this->signature;
    }

    /**
     * Get the signature algorithm
     */
    public function getAlgorithm(): string
    {
        return $this->algorithm;
    }

    /**
     * Get the hash algorithm used
     */
    public function getHashAlgorithm(): string
    {
        return $this->hashAlgorithm;
    }

    /**
     * Get the key size in bits
     */
    public function getKeySize(): int
    {
        return $this->keySize;
    }

    /**
     * Get the curve name (ECDSA only)
     */
    public function getCurve(): ?string
    {
        return $this->curve;
    }

    /**
     * Get signature as Base64 string
     */
    public function getSignatureBase64(): string
    {
        return base64_encode($this->signature);
    }

    /**
     * Get signature as hexadecimal string
     */
    public function getSignatureHex(): string
    {
        return bin2hex($this->signature);
    }

    /**
     * Check if this is an RSA signature
     */
    public function isRsa(): bool
    {
        return $this->algorithm === 'rsa';
    }

    /**
     * Check if this is an ECDSA signature
     */
    public function isEcdsa(): bool
    {
        return $this->algorithm === 'ecdsa';
    }

    /**
     * Get signature length in bytes
     */
    public function getSignatureLength(): int
    {
        return strlen($this->signature);
    }

    /**
     * Export to array (for serialization)
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        $data = [
            'signature' => $this->getSignatureBase64(),
            'algorithm' => $this->algorithm,
            'hash_algorithm' => $this->hashAlgorithm,
            'key_size' => $this->keySize,
        ];

        if ($this->curve !== null) {
            $data['curve'] = $this->curve;
        }

        return $data;
    }

    /**
     * Create from array (for deserialization)
     * @param array<string, mixed> $data
     */
    public static function fromArray(array $data): self
    {
        $requiredFields = ['signature', 'algorithm', 'hash_algorithm', 'key_size'];

        foreach ($requiredFields as $field) {
            if (! isset($data[$field])) {
                throw new InvalidArgumentException("Missing required field: {$field}");
            }
        }

        $signature = base64_decode($data['signature'], true);
        if ($signature === false) {
            throw new InvalidArgumentException('Invalid Base64 signature');
        }

        return new self(
            signature: $signature,
            algorithm: $data['algorithm'],
            hashAlgorithm: $data['hash_algorithm'],
            keySize: (int)$data['key_size'],
            curve: $data['curve'] ?? null
        );
    }

    /**
     * Create from Base64 signature
     */
    public static function fromBase64(
        string $base64Signature,
        string $algorithm,
        string $hashAlgorithm,
        int $keySize,
        ?string $curve = null
    ): self {
        $signature = base64_decode($base64Signature, true);
        if ($signature === false) {
            throw new InvalidArgumentException('Invalid Base64 signature');
        }

        return new self(
            signature: $signature,
            algorithm: $algorithm,
            hashAlgorithm: $hashAlgorithm,
            keySize: $keySize,
            curve: $curve
        );
    }

    /**
     * Create from hexadecimal signature
     */
    public static function fromHex(
        string $hexSignature,
        string $algorithm,
        string $hashAlgorithm,
        int $keySize,
        ?string $curve = null
    ): self {
        $signature = hex2bin($hexSignature);
        if ($signature === false) {
            throw new InvalidArgumentException('Invalid hexadecimal signature');
        }

        return new self(
            signature: $signature,
            algorithm: $algorithm,
            hashAlgorithm: $hashAlgorithm,
            keySize: $keySize,
            curve: $curve
        );
    }

    /**
     * Get signature description
     */
    public function getDescription(): string
    {
        $description = strtoupper($this->algorithm);

        if ($this->algorithm === 'rsa') {
            $description .= " {$this->keySize}-bit";
        } elseif ($this->algorithm === 'ecdsa') {
            $description .= " {$this->curve}";
        }

        $description .= " with " . strtoupper($this->hashAlgorithm);

        return $description;
    }

    /**
     * Get summary information (safe for logging)
     * @return array<string, mixed>
     */
    public function getSummary(): array
    {
        return [
            'algorithm' => $this->algorithm,
            'hash_algorithm' => $this->hashAlgorithm,
            'key_size' => $this->keySize,
            'curve' => $this->curve,
            'signature_length' => $this->getSignatureLength(),
            'description' => $this->getDescription(),
        ];
    }
}
