<?php

namespace App\Http\Middleware;

use App\Facades\Feature;
use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Franchise;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\Traits\HasDealershipAccessScope;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleOffer;
use Symfony\Component\HttpFoundation\Response;

class DealershipAccessMiddleware
{
    /**
     * Models that should have dealership access scoping applied.
     */
    protected array $scopedModels = [
        Vehicle::class,
        VehicleOffer::class,
        NewCar::class,
        Dealership::class,
        Franchise::class,
    ];

    /**
     * Map of route parameter names to their corresponding model classes.
     */
    protected array $modelMap = [
        'vehicle' => Vehicle::class,
        'vehicle_offer' => VehicleOffer::class,
        'vehicleOffer' => VehicleOffer::class,
        'new_car' => NewCar::class,
        'newCar' => NewCar::class,
        'dealership' => Dealership::class,
        'franchise' => Franchise::class,
    ];

    /**
     * Handle an incoming request.
     *
     * This middleware handles two concerns:
     * 1. Applies global scopes to filter queries based on user's dealership access (for index operations)
     * 2. When a specific model parameter is provided, checks explicit access and returns 403 if unauthorized
     *
     * @param string|null $modelParam Route parameter name (e.g., 'vehicle', 'vehicle_offer')
     */
    public function handle(Request $request, Closure $next, ?string $modelParam = null): Response
    {
        if (!Feature::isEnabled('dealership-access')) {
            return $next($request);
        }

        $user = $request->user();

        if (!$user) {
            abort(403, 'Unauthorized');
        }

        // Always apply global scopes for index/list filtering
        $this->applyGlobalScopes($user);

        // If a specific model parameter is provided, check explicit access
        if ($modelParam) {
            $this->checkResourceAccess($request, $user, $modelParam);
        }

        return $next($request);
    }

    /**
     * Apply global scopes to all dealership-related models.
     * This filters queries to only return records the user has access to.
     */
    protected function applyGlobalScopes($user): void
    {
        collect($this->scopedModels)->each(function ($modelClass) use ($user) {
            $modelClass::addGlobalScope(HasDealershipAccessScope::class, function ($builder) use ($user) {
                $builder->forUser($user);
            });
        });
    }

    /**
     * Check if the user has access to the specific resource.
     */
    protected function checkResourceAccess(Request $request, $user, string $modelParam): void
    {
        $model = $this->resolveModel($request, $modelParam);

        if (!$model) {
            return;
        }

        $dealershipId = $this->getDealershipId($model);

        if ($dealershipId && !$user->hasAccessToDealership($dealershipId)) {
            abort(403, 'You do not have access to this dealership');
        }

        if (!$dealershipId && isset($model->franchise_id) && $model->franchise_id) {
            if (!$user->hasAccessToFranchise($model->franchise_id)) {
                abort(403, 'You do not have access to this franchise');
            }
        }
    }

    /**
     * Resolve the model from route parameter.
     * Handles both route model binding (Model instance) and raw IDs.
     * Uses withoutGlobalScope() to bypass dealership filtering and find the actual record.
     */
    protected function resolveModel(Request $request, string $modelParam): ?Model
    {
        $value = $request->route($modelParam);

        if (!$value) {
            return null;
        }

        // Already resolved via route model binding
        if ($value instanceof Model) {
            return $value;
        }

        // Raw ID - resolve from database without global scopes
        $modelClass = $this->modelMap[$modelParam] ?? null;

        if (!$modelClass) {
            return null;
        }

        return $modelClass::query()->withoutGlobalScope(HasDealershipAccessScope::class)->find($value);
    }

    /**
     * Get the dealership ID from the model.
     */
    protected function getDealershipId(Model $model): ?int
    {
        // If it's a Dealership model, use its own ID
        if ($model instanceof Dealership) {
            return $model->id;
        }

        // Otherwise, check for dealership_id attribute
        if (isset($model->dealership_id)) {
            return $model->dealership_id;
        }

        return null;
    }
}
