<?php

namespace App\Http\Middleware;

use App\Master\Models\ApiToken;
use Carbon\Carbon;
use Closure;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Http\Request;
use Mtc\MercuryDataModels\Jobs\TrackApiRequest;
use Symfony\Component\HttpFoundation\Response;

class ApiAuthenticatedRequest
{
    use DispatchesJobs;

    private ApiToken $apiToken;

    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next, ?string $legacyScenario = null): Response
    {
        if ($this->failsAuthToken($request, $legacyScenario)) {
            return \response('Access Denied', 401);
        }

        $start = microtime(true);
        $response =  $next($request);
        $request_length = microtime(true) - $start;

        if (isset($this->apiToken)) {
            $this->dispatch(new TrackApiRequest(
                tenant('id'),
                $request->getRequestUri(),
                $request->input(),
                $request->method(),
                $response->getStatusCode(),
                $request_length,
                $this->apiToken,
            ));
        }
        return $response;
    }

    private function failsAuthToken(Request $request, ?string $legacyScenario): bool
    {
        if (!$request->hasHeader('Authorization') && empty($legacyScenario)) {
            return true;
        }

        if (!empty($legacyScenario)) {
            return $this->authenticatesLegacyScenario($request, $legacyScenario) === false;
        }

        $token = str_replace('Bearer ', '', $request->header('Authorization'));
        return $this->matchesApiToken($token) === false;
    }

    private function authenticatesLegacyScenario(Request $request, string $legacyScenario): bool
    {
        return match ($legacyScenario) {
            'auto-imaging' => $request->input('key') === config('services.image-sync.auto-imaging.key')
                || $this->matchesApiToken($request->input('key')),
            default => false
        };
    }

    private function matchesApiToken(?string $token): bool
    {
        $apiToken = ApiToken::query()
            ->whereHas('apiUser', fn($query) => $query->where('is_active', 1))
            ->where('token', $token)
            ->where('expires_at', '>=', Carbon::now())
            ->first();
        if (!$apiToken) {
            return false;
        }

        $has_access_to_tenant = $apiToken->apiUser->tenants()->where('tenant_id', tenant('id'))->exists();
        if (!$has_access_to_tenant) {
            return false;
        }

        $this->apiToken = $apiToken;
        return true;
    }
}
