<?php

declare(strict_types=1);

namespace App\Framework\Vault;

use App\Framework\Database\ConnectionInterface;
use App\Framework\Http\IpAddress;
use App\Framework\UserAgent\UserAgent;
use App\Framework\Vault\ValueObjects\SecretKey;
use App\Framework\Vault\ValueObjects\VaultAction;
use App\Framework\Vault\ValueObjects\VaultAuditEntry;
use DateTimeImmutable;

/**
 * Audit Logger für Vault Operations
 *
 * Schreibt alle Vault-Zugriffe in vault_audit_log Tabelle
 */
final readonly class VaultAuditLogger
{
    public function __construct(
        private ConnectionInterface $connection
    ) {
    }

    public function log(VaultAuditEntry $entry): void
    {
        try {
            $data = $entry->toArray();

            $sql = 'INSERT INTO vault_audit_log (secret_key, action, user_id, ip_address, user_agent, success, error_message, timestamp)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?)';

            $params = [
                $data['secret_key'],
                $data['action'],
                $data['user_id'] ?? null,
                $data['ip_address'] ?? null,
                $data['user_agent'] ?? null,
                $data['success'] ? 1 : 0,
                $data['error_message'] ?? null,
                $data['timestamp'] ?? date('Y-m-d H:i:s'),
            ];

            $query = \App\Framework\Database\ValueObjects\SqlQuery::create($sql, $params);
            $this->connection->execute($query);
        } catch (\Throwable $e) {
            // Audit Logging sollte niemals Operations blockieren
            // Aber wir loggen den Fehler für Monitoring
            error_log("Failed to write vault audit log: {$e->getMessage()}");
        }
    }

    /**
     * Audit Log für einen Secret Key abrufen
     *
     * @return array<VaultAuditEntry>
     */
    public function getAuditLog(string $secretKey, int $limit = 100): array
    {
        $sql = 'SELECT * FROM vault_audit_log
                WHERE secret_key = ?
                ORDER BY timestamp DESC
                LIMIT ?';

        $query = \App\Framework\Database\ValueObjects\SqlQuery::create($sql, [$secretKey, $limit]);
        $result = $this->connection->query($query);

        $entries = [];
        foreach ($result as $row) {
            $entries[] = $this->hydrateEntry($row);
        }

        return $entries;
    }

    /**
     * Gesamten Audit Log abrufen (mit Pagination)
     *
     * @return array<VaultAuditEntry>
     */
    public function getRecentAuditLog(int $limit = 100, int $offset = 0): array
    {
        $sql = 'SELECT * FROM vault_audit_log
                ORDER BY timestamp DESC
                LIMIT ? OFFSET ?';

        $query = \App\Framework\Database\ValueObjects\SqlQuery::create($sql, [$limit, $offset]);
        $result = $this->connection->query($query);

        $entries = [];
        foreach ($result as $row) {
            $entries[] = $this->hydrateEntry($row);
        }

        return $entries;
    }

    private function hydrateEntry(array $row): VaultAuditEntry
    {
        return new VaultAuditEntry(
            key: new SecretKey($row['secret_key']),
            action: VaultAction::from($row['action']),
            timestamp: new DateTimeImmutable($row['timestamp']),
            userId: $row['user_id'],
            ipAddress: $row['ip_address'] ? IpAddress::fromString($row['ip_address']) : null,
            userAgent: $row['user_agent'] ? new UserAgent($row['user_agent']) : null,
            success: (bool) $row['success'],
            errorMessage: $row['error_message']
        );
    }
}
