<?php
declare(strict_types=1);
namespace App\Platform\Soc\Event;

final readonly class SecurityEventRecord
{
    /** @param array<string,mixed> $metadata */
    public function __construct(
        public string $uuid,
        public EventSeverity $severity,
        public string $category,
        public string $module,
        public string $action,
        public string $result,
        public string $message,
        public ?int $userId,
        public ?int $organisationId,
        public ?string $sessionReference,
        public ?string $ipHash,
        public ?string $deviceHash,
        public ?string $subjectType,
        public ?string $subjectReference,
        public ?string $requestId,
        public array $metadata,
        public string $occurredAt,
    ) {}

    /** @param array<string,mixed> $metadata */
    public static function create(
        EventSeverity $severity,
        string $category,
        string $module,
        string $action,
        string $result,
        string $message,
        array $metadata = [],
        ?string $subjectType = null,
        ?string $subjectReference = null,
    ): self {
        return new self(
            self::uuidV4(),
            $severity,
            self::clean($category, 80),
            self::clean($module, 80),
            self::clean($action, 120),
            self::clean($result, 40),
            self::clean($message, 1000),
            isset($_SESSION['user_id']) ? (int) $_SESSION['user_id'] : null,
            isset($_SESSION['organisation_id']) ? (int) $_SESSION['organisation_id'] : null,
            self::sessionReference(),
            self::hashContext((string) ($_SERVER['REMOTE_ADDR'] ?? '')),
            self::hashContext((string) ($_SERVER['HTTP_USER_AGENT'] ?? '')),
            $subjectType !== null ? self::clean($subjectType, 80) : null,
            $subjectReference !== null ? self::clean($subjectReference, 190) : null,
            isset($_SERVER['AXIOM_REQUEST_ID']) ? self::clean((string) $_SERVER['AXIOM_REQUEST_ID'], 80) : null,
            EventSanitizer::sanitize($metadata),
            gmdate('Y-m-d H:i:s'),
        );
    }

    private static function clean(string $value, int $length): string
    {
        return mb_substr(trim($value), 0, $length);
    }

    private static function sessionReference(): ?string
    {
        $id = session_id();
        return $id !== '' ? hash('sha256', $id) : null;
    }

    private static function hashContext(string $value): ?string
    {
        if ($value === '') return null;
        $salt = (string) ($_ENV['SOC_EVENT_HASH_SALT'] ?? 'axiom-soc-context-v1');
        return hash_hmac('sha256', $value, $salt);
    }

    private static function uuidV4(): string
    {
        $bytes = random_bytes(16);
        $bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40);
        $bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80);
        $hex = bin2hex($bytes);
        return substr($hex, 0, 8) . '-' . substr($hex, 8, 4) . '-' . substr($hex, 12, 4) . '-' . substr($hex, 16, 4) . '-' . substr($hex, 20);
    }
}
