<?php

declare(strict_types=1);

namespace Blax\WebRtc\Recording;

use Blax\WebRtc\Contracts\RecordingStore;

/**
 * Stores each participant's streamed audio as an appendable file per call:
 * `<directory>/<room>/<peer>.<ext>`. The browser MediaRecords its mic and streams
 * the chunks (default webm/opus) over the WebSocket; concatenating them yields a
 * single playable file, so this just appends bytes and closes on finalize.
 */
final class FileRecordingStore implements RecordingStore
{
    /** @var array<string,resource> open write handles keyed by "room|peer" */
    private array $handles = [];

    public function __construct(
        private string $directory,
        private string $extension = 'webm',
    ) {}

    public function append(string $roomId, string $peerId, string $chunk): void
    {
        $key = $this->key($roomId, $peerId);

        if (! isset($this->handles[$key])) {
            $dir = $this->directory.'/'.$this->safe($roomId);
            if (! is_dir($dir)) {
                @mkdir($dir, 0775, true);
            }
            $handle = @fopen($this->path($roomId, $peerId), 'ab');
            if ($handle === false) {
                return; // storage unavailable — drop the chunk rather than crash the call
            }
            $this->handles[$key] = $handle;
        }

        fwrite($this->handles[$key], $chunk);
    }

    public function finalize(string $roomId, string $peerId): ?string
    {
        $key = $this->key($roomId, $peerId);
        if (! isset($this->handles[$key])) {
            return null;
        }

        fclose($this->handles[$key]);
        unset($this->handles[$key]);

        return $this->path($roomId, $peerId);
    }

    public function discard(string $roomId, string $peerId): void
    {
        $key = $this->key($roomId, $peerId);
        if (isset($this->handles[$key])) {
            fclose($this->handles[$key]);
            unset($this->handles[$key]);
        }

        $path = $this->path($roomId, $peerId);
        if (is_file($path)) {
            @unlink($path);
        }
    }

    /** The path a recording is (or will be) written to. */
    public function path(string $roomId, string $peerId): string
    {
        return $this->directory.'/'.$this->safe($roomId).'/'.$this->safe($peerId).'.'.$this->extension;
    }

    private function key(string $roomId, string $peerId): string
    {
        return $roomId.'|'.$peerId;
    }

    private function safe(string $value): string
    {
        return preg_replace('/[^A-Za-z0-9_.-]/', '_', $value) ?? '_';
    }
}
