<?php

declare(strict_types=1);

namespace App\Base;

use Illuminate\Contracts\Cache\Lock;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

abstract class AbstractController extends Controller
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    protected ?Lock $Lock = null;

    public function __construct()
    {
        Log::info(static::class);
    }

    public function acquireLock(int|string $UserId): Lock
    {
        $this->Lock = $this->makeLock($UserId);
        if ($this->Lock->block(1)) {
            return $this->Lock;
        }

        throw new BadRequestHttpException(
            'Failed to acquire the lock: '.$this->makeLockKey($UserId),
            null,
            Response::HTTP_LOCKED,
        );
    }

    public function makeLock(int|string $UserId): Lock
    {
        $Method = 'makeLock';
        Log::info(static::class, compact('UserId', 'Method'));

        return Cache::lock($this->makeLockKey($UserId), 33);
    }

    public function makeLockKey(int|string $UserId): string
    {
        return static::class.":$UserId";
    }

    public function releaseLock(): void
    {
        if ($this->Lock) {
            if (App::runningUnitTests()) {
                $this->Lock->release();

                return;
            }

            App::terminating(function () {
                if ($this->Lock) {
                    sleep(1);
                    $this->Lock->release();
                }
            });
        }
    }
}
