<?php

namespace Tests\Feature\Controllers;

use Database\Seeders\Global\GlobalRoleSeed;
use PHPUnit\Framework\Attributes\DataProvider;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\User;
use Mtc\MercuryDataModels\Vehicle;
use Tests\DatabaseTestCase;
use Tests\UserForTenant;

/**
 * AUTO-1678 - tenant write endpoints must be gated by their section permission,
 * not just Auth::check(). Each case asserts a representative write endpoint
 * returns 403 for an authenticated user without the permission and passes the
 * gate (no 403) once the permission is granted.
 */
class TenantRoutePermissionTest extends DatabaseTestCase
{
    use UserForTenant;

    public static function gatedWriteEndpointProvider(): array
    {
        return [
            'new car store requires edit-new-vehicles' => [
                'edit-new-vehicles',
                'postJson',
                fn () => route('tenant.new-cars.store'),
                ['name' => 'permission probe'],
            ],
            'dealership store requires edit-dealerships' => [
                'edit-dealerships',
                'postJson',
                fn () => route('tenant.dealerships.store'),
                [],
            ],
            'franchise store requires edit-franchises' => [
                'edit-franchises',
                'postJson',
                fn () => route('tenant.franchises.store'),
                [],
            ],
            'offer type store requires edit-offers' => [
                'edit-offers',
                'postJson',
                fn () => route('tenant.offer-types.store'),
                [],
            ],
            'seo data store requires edit-seo' => [
                'edit-seo',
                'postJson',
                fn () => route('tenant.seo_settings.data.store'),
                [],
            ],
            'settings update requires manage-settings' => [
                'manage-settings',
                'putJson',
                fn () => route('tenant.settings.update', ['section' => 'general']),
                [],
            ],
            'label store requires edit-vehicles' => [
                'edit-vehicles',
                'postJson',
                fn () => route('tenant.labels.store'),
                [],
            ],
            'colour map update requires edit-vehicles' => [
                'edit-vehicles',
                'putJson',
                fn () => route('tenant.colour-map.update'),
                [],
            ],
        ];
    }

    #[DataProvider('gatedWriteEndpointProvider')]
    public function testWriteEndpointRequiresPermission(
        string $permission,
        string $method,
        \Closure $routeResolver,
        array $payload,
    ): void {
        $route = $routeResolver();

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->{$method}($route, $payload);

        $response->assertStatus(403);

        $response = $this->actingAs($this->userWithPermission($permission))
            ->withHeader('X-Tenant', tenant('id'))
            ->{$method}($route, $payload);

        $this->assertNotEquals(
            403,
            $response->status(),
            "Endpoint {$route} still rejects a user holding {$permission}",
        );
    }

    public function testBulkDeleteRequiresPermissionForTargetModel(): void
    {
        $vehicle = Vehicle::factory()->create();

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->deleteJson(route('tenant.bulk-delete'), [
                'model' => 'vehicles',
                'ids' => [$vehicle->id],
            ]);

        $response->assertStatus(403);
        $this->assertTrue(Vehicle::query()->where('id', $vehicle->id)->exists());
    }

    public function testBulkDeletePermissionIsPerModel(): void
    {
        $newCar = NewCar::factory()->create();
        $user = $this->userWithPermission('edit-vehicles');

        // edit-vehicles does not allow deleting other model types
        $response = $this->actingAs($user)
            ->withHeader('X-Tenant', tenant('id'))
            ->deleteJson(route('tenant.bulk-delete'), [
                'model' => 'new-cars',
                'ids' => [$newCar->id],
            ]);

        $response->assertStatus(403);
        $this->assertTrue(NewCar::query()->where('id', $newCar->id)->exists());
    }

    public function testBulkDeleteAllowedWithMatchingPermission(): void
    {
        $vehicle = Vehicle::factory()->create();

        $response = $this->actingAs($this->userWithPermission('edit-vehicles'))
            ->withHeader('X-Tenant', tenant('id'))
            ->deleteJson(route('tenant.bulk-delete'), [
                'model' => 'vehicles',
                'ids' => [$vehicle->id],
            ]);

        $response->assertStatus(200);
        $this->assertFalse(Vehicle::query()->where('id', $vehicle->id)->exists());
    }

    private function userWithPermission(string $permission): User
    {
        $this->seed(GlobalRoleSeed::class);
        $user = $this->getUser(false);
        $user->unsetRelation('permissions');
        $user->givePermissionTo($permission);

        return $user->load('permissions');
    }
}
