<?php

declare(strict_types=1);

namespace App\Services\Uploads;

use App\Repositories\Uploads\UploadSessionRepository;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use RuntimeException;

final class UploadCancelService
{
    public function __construct(
        private readonly UploadSessionRepository $sessions,
        private readonly UploadStorageService $storage,
        private readonly UploadAuditService $audit
    ) {
    }

    public function cancel(string $sessionId, int $userId, string $ip): array
    {
        $session = $this->sessions->getOrFail($sessionId);

        $lockSeconds = (int) config('uploads.chunk.lock_seconds', 10);
        $lockWait = (int) config('uploads.chunk.lock_wait_seconds', 5);
        $lock = Cache::lock('uploads:cancel:' . $sessionId, $lockSeconds);

        try {
            $lock->block($lockWait);
        } catch (\Throwable $e) {
            $this->audit->log($userId, 'upload_cancel_locked', $ip, [
                'session_id' => $sessionId,
            ]);
            throw new RuntimeException('Upload is busy. Please retry.');
        }

        try {
            if (in_array($session->status, [UploadSessionStatus::CANCELLED, UploadSessionStatus::PROCESSED, UploadSessionStatus::QUARANTINED], true)) {
                $this->audit->log($userId, 'upload_cancel_noop', $ip, [
                    'session_id' => $sessionId,
                    'status' => $session->status,
                ]);

                return [
                    'session_id' => $sessionId,
                    'status' => $session->status,
                ];
            }

            $this->safeDeleteTmp($session->tempPath);

            $this->sessions->updateStatus($sessionId, UploadSessionStatus::CANCELLED);
            $this->sessions->updateProgress($sessionId, 0);
            $this->sessions->updateFailureReason($sessionId, 'cancelled');

            $this->audit->log($userId, 'upload_cancelled', $ip, [
                'session_id' => $sessionId,
            ]);

            return [
                'session_id' => $sessionId,
                'status' => UploadSessionStatus::CANCELLED,
            ];
        } finally {
            optional($lock)->release();
        }
    }

    private function safeDeleteTmp(string $path): void
    {
        $tmpRoot = $this->storage->sectionPath('tmp');
        $realRoot = realpath($tmpRoot);
        $realPath = realpath($path);

        if (! $realRoot || ! $realPath || strpos($realPath, $realRoot) !== 0) {
            return;
        }

        if (File::exists($realPath)) {
            File::delete($realPath);
        }
    }
}
