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

use App\Core\Database;
use PDO;

final class EventStorage
{
    public function store(SecurityEventRecord $event): void
    {
        $statement = Database::connection()->prepare(
            'INSERT INTO system_security_events '
            . '(event_uuid,severity,category,module,action,result,message,user_id,organisation_id,session_reference,ip_hash,device_hash,subject_type,subject_reference,request_id,metadata_json,occurred_at,created_at) '
            . 'VALUES (:event_uuid,:severity,:category,:module,:action,:result,:message,:user_id,:organisation_id,:session_reference,:ip_hash,:device_hash,:subject_type,:subject_reference,:request_id,:metadata_json,:occurred_at,NOW())'
        );
        $statement->execute($this->parameters($event));
    }

    public function enqueue(SecurityEventRecord $event): void
    {
        $statement = Database::connection()->prepare(
            'INSERT INTO system_event_queue '
            . '(event_uuid,payload_json,status,attempts,available_at,created_at,updated_at) '
            . 'VALUES (:event_uuid,:payload_json,\'pending\',0,NOW(),NOW(),NOW()) '
            . 'ON DUPLICATE KEY UPDATE updated_at=VALUES(updated_at)'
        );
        $statement->execute([
            'event_uuid' => $event->uuid,
            'payload_json' => json_encode($this->payload($event), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR),
        ]);
    }

    /** @return list<array<string,mixed>> */
    public function pending(int $limit = 100): array
    {
        $limit = max(1, min(500, $limit));
        $statement = Database::connection()->prepare(
            'SELECT id,event_uuid,payload_json,attempts FROM system_event_queue '
            . 'WHERE status=\'pending\' AND available_at<=NOW() ORDER BY id ASC LIMIT :limit'
        );
        $statement->bindValue('limit', $limit, PDO::PARAM_INT);
        $statement->execute();
        return $statement->fetchAll() ?: [];
    }

    public function markProcessed(int $id): void
    {
        $statement = Database::connection()->prepare(
            'UPDATE system_event_queue SET status=\'processed\',processed_at=NOW(),updated_at=NOW() WHERE id=:id'
        );
        $statement->execute(['id' => $id]);
    }

    public function markFailed(int $id, string $message): void
    {
        $statement = Database::connection()->prepare(
            'UPDATE system_event_queue SET attempts=attempts+1,last_error=:last_error,available_at=DATE_ADD(NOW(),INTERVAL 5 MINUTE),updated_at=NOW() WHERE id=:id'
        );
        $statement->execute(['id' => $id, 'last_error' => mb_substr($message, 0, 1000)]);
    }

    /** @return array<string,mixed> */
    private function parameters(SecurityEventRecord $event): array
    {
        return [
            'event_uuid'=>$event->uuid,'severity'=>$event->severity->value,'category'=>$event->category,
            'module'=>$event->module,'action'=>$event->action,'result'=>$event->result,'message'=>$event->message,
            'user_id'=>$event->userId,'organisation_id'=>$event->organisationId,'session_reference'=>$event->sessionReference,
            'ip_hash'=>$event->ipHash,'device_hash'=>$event->deviceHash,'subject_type'=>$event->subjectType,
            'subject_reference'=>$event->subjectReference,'request_id'=>$event->requestId,
            'metadata_json'=>json_encode($event->metadata, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR),
            'occurred_at'=>$event->occurredAt,
        ];
    }

    /** @return array<string,mixed> */
    private function payload(SecurityEventRecord $event): array
    {
        return $this->parameters($event) + ['severity' => $event->severity->value];
    }
}
