<?php

declare(strict_types=1);

namespace App\Framework\Security\RequestSigning;

/**
 * Represents a request signature with metadata
 */
final readonly class RequestSignature
{
    public function __construct(
        public string $signature,
        public string $keyId,
        public SigningAlgorithm $algorithm,
        public array $headers,
        public \DateTimeImmutable $timestamp,
        public ?int $expires = null,
    ) {
    }

    /**
     * Check if the signature has expired
     */
    public function isExpired(?\DateTimeImmutable $now = null): bool
    {
        if ($this->expires === null) {
            return false;
        }

        $now ??= new \DateTimeImmutable();
        $expirationTime = $this->timestamp->modify("+{$this->expires} seconds");

        return $now > $expirationTime;
    }

    /**
     * Create from HTTP Signature header
     * Format: keyId="key-1",algorithm="hmac-sha256",headers="(request-target) host date",signature="base64signature"
     */
    public static function fromHttpSignatureHeader(string $header): self
    {
        $matches = [];
        if (! preg_match_all('/(\w+)="([^"]+)"/', $header, $matches, PREG_SET_ORDER)) {
            throw new \InvalidArgumentException('Invalid HTTP Signature header format');
        }

        $params = [];
        foreach ($matches as $match) {
            $params[$match[1]] = $match[2];
        }

        $requiredParams = ['keyId', 'algorithm', 'signature'];
        foreach ($requiredParams as $param) {
            if (! isset($params[$param])) {
                throw new \InvalidArgumentException("Missing required parameter: {$param}");
            }
        }

        $algorithm = SigningAlgorithm::tryFrom($params['algorithm']);
        if ($algorithm === null) {
            throw new \InvalidArgumentException('Unsupported signing algorithm: ' . $params['algorithm']);
        }

        $headers = isset($params['headers']) ? explode(' ', $params['headers']) : ['date'];

        return new self(
            signature: $params['signature'],
            keyId: $params['keyId'],
            algorithm: $algorithm,
            headers: $headers,
            timestamp: new \DateTimeImmutable(),
            expires: isset($params['expires']) ? (int)$params['expires'] : null,
        );
    }

    /**
     * Convert to HTTP Signature header format
     */
    public function toHttpSignatureHeader(): string
    {
        $parts = [
            'keyId="' . $this->keyId . '"',
            'algorithm="' . $this->algorithm->value . '"',
            'headers="' . implode(' ', $this->headers) . '"',
            'signature="' . $this->signature . '"',
        ];

        if ($this->expires !== null) {
            $parts[] = 'expires="' . $this->expires . '"';
        }

        return implode(',', $parts);
    }
}
