<?php

declare(strict_types=1);

namespace App\Framework\Security\RequestSigning;

use App\Framework\Http\HttpRequest;
use App\Framework\Logging\Logger;

/**
 * High-level service for request signing functionality
 */
final readonly class RequestSigningService
{
    public function __construct(
        private RequestSigner $signer,
        private RequestVerifier $verifier,
        private SigningKeyRepository $keyRepository,
        private RequestSigningConfig $config,
        private Logger $logger,
    ) {
    }

    /**
     * Sign an outgoing HTTP request
     */
    public function signOutgoingRequest(
        HttpRequest $request,
        ?string $keyId = null,
        ?array $headers = null,
        ?int $expires = null,
    ): HttpRequest {
        if (! $this->config->enabled) {
            return $request;
        }

        $key = $this->getSigningKey($keyId);
        if ($key === null) {
            $this->logger->warning('No signing key available for outgoing request', [
                'key_id' => $keyId,
                'path' => $request->path,
            ]);

            return $request;
        }

        $headers ??= $this->config->defaultHeaders;
        $expires ??= $this->config->defaultExpiry;

        try {
            return $this->signer->signRequest($request, $key, $headers, $expires);
        } catch (\Exception $e) {
            $this->logger->error('Failed to sign outgoing request', [
                'error' => $e->getMessage(),
                'key_id' => $key->keyId,
                'path' => $request->path,
            ]);

            return $request;
        }
    }

    /**
     * Verify an incoming HTTP request
     */
    public function verifyIncomingRequest(HttpRequest $request): VerificationResult
    {
        if (! $this->config->enabled) {
            return VerificationResult::success(
                new RequestSignature('', '', SigningAlgorithm::HMAC_SHA256, [], new \DateTimeImmutable()),
                SigningKey::generateHmac('disabled', SigningAlgorithm::HMAC_SHA256)
            );
        }

        try {
            $result = $this->verifier->verify($request);

            if ($result->isSuccess()) {
                $this->logger->info('Request signature verified successfully', [
                    'key_id' => $result->signature->keyId,
                    'algorithm' => $result->signature->algorithm->value,
                    'path' => $request->path,
                ]);
            } else {
                $this->logger->warning('Request signature verification failed', [
                    'error' => $result->errorMessage,
                    'path' => $request->path,
                ]);
            }

            return $result;
        } catch (\Exception $e) {
            $this->logger->error('Request signature verification error', [
                'error' => $e->getMessage(),
                'path' => $request->path,
            ]);

            return VerificationResult::failure('Verification error: ' . $e->getMessage());
        }
    }

    /**
     * Add a new signing key
     */
    public function addSigningKey(SigningKey $key): void
    {
        $this->keyRepository->store($key);

        $this->logger->info('Signing key added', [
            'key_id' => $key->keyId,
            'algorithm' => $key->algorithm->value,
            'expires_at' => $key->expiresAt?->format('Y-m-d H:i:s'),
        ]);
    }

    /**
     * Remove a signing key
     */
    public function removeSigningKey(string $keyId): void
    {
        $this->keyRepository->remove($keyId);

        $this->logger->info('Signing key removed', [
            'key_id' => $keyId,
        ]);
    }

    /**
     * Rotate a signing key
     */
    public function rotateSigningKey(string $keyId, SigningKey $newKey): void
    {
        $this->keyRepository->rotateKey($keyId, $newKey);

        $this->logger->info('Signing key rotated', [
            'old_key_id' => $keyId,
            'new_key_id' => $newKey->keyId,
            'algorithm' => $newKey->algorithm->value,
        ]);
    }

    /**
     * Get all active signing keys
     */
    public function getActiveKeys(): array
    {
        return $this->keyRepository->getAllActive();
    }

    /**
     * Generate a new HMAC signing key
     */
    public function generateHmacKey(
        string $keyId,
        SigningAlgorithm $algorithm = SigningAlgorithm::HMAC_SHA256,
        ?\DateTimeImmutable $expiresAt = null,
    ): SigningKey {
        $key = SigningKey::generateHmac($keyId, $algorithm, $expiresAt);
        $this->addSigningKey($key);

        return $key;
    }

    /**
     * Create an RSA signing key
     */
    public function createRsaKey(
        string $keyId,
        string $privateKey,
        ?\DateTimeImmutable $expiresAt = null,
    ): SigningKey {
        $key = SigningKey::createRsa($keyId, $privateKey, $expiresAt);
        $this->addSigningKey($key);

        return $key;
    }

    /**
     * Get a signing key by ID or return the default key
     */
    private function getSigningKey(?string $keyId = null): ?SigningKey
    {
        if ($keyId !== null) {
            return $this->keyRepository->findByKeyId($keyId);
        }

        // Get the first active key as default
        $activeKeys = $this->keyRepository->getAllActive();

        return reset($activeKeys) ?: null;
    }
}
