<?php

namespace Tests\Feature\Controllers;

use App\Http\Middleware\EnforcePropertyCategoryRestriction;
use App\Scopes\PropertyCategoryVisibilityScope;
use Illuminate\Http\Request;
use Mtc\MercuryDataModels\Property;
use Mtc\MercuryDataModels\PropertyCategory;
use Tests\DatabaseTestCase;
use Tests\UserForTenant;

class PropertyCategoryRestrictionTest extends DatabaseTestCase
{
    use UserForTenant;

    /**
     * A non-MTC admin. The Administrator role grants manage-properties (record
     * routes) but deliberately NOT manage-property-categories (definition
     * routes are mtc-only by the permission model), so this user exercises the
     * record-level and visibility behaviour the restriction flag adds on top.
     */
    private function nonMtcUser(): \Mtc\MercuryDataModels\User
    {
        $user = $this->getUser('Administrator');
        $user->givePermissionTo('manage-properties');
        $user->load('permissions');

        return $user;
    }

    /**
     * Create a data module (property category) with the restriction flag
     * persisted, optionally with a record so nested-route bindings resolve.
     */
    private function makeCategory(bool $restricted): PropertyCategory
    {
        $category = PropertyCategory::factory()->create();
        $category->superadmin_only = $restricted;
        $category->save();

        return $category;
    }

    private function writeRequest($user): Request
    {
        $request = Request::create('/', 'POST', ['name' => 'x', 'superadmin_only' => true]);
        $request->setUserResolver(fn () => $user);

        return $request;
    }

    public function testIndexExcludesRestrictedModuleForNonMtcUser()
    {
        PropertyCategory::query()->delete();
        $this->makeCategory(false);
        $this->makeCategory(true);

        $response = $this->actingAs($this->nonMtcUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.property-categories.index'));

        $response->assertStatus(200);
        $this->assertCount(1, $response->json());
    }

    public function testIndexIncludesRestrictedModuleForMtcUser()
    {
        PropertyCategory::query()->delete();
        $this->makeCategory(false);
        $this->makeCategory(true);

        $response = $this->actingAs($this->getUser('mtc'))
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.property-categories.index'));

        $response->assertStatus(200);
        $this->assertCount(2, $response->json());
    }

    public function testDestroyRestrictedModuleAllowedForMtcUser()
    {
        $category = $this->makeCategory(true);

        $response = $this->actingAs($this->getUser('mtc'))
            ->withHeader('X-Tenant', tenant('id'))
            ->deleteJson(route('tenant.property-categories.destroy', $category));

        $response->assertStatus(200);
        $this->assertFalse(PropertyCategory::query()->whereKey($category->id)->exists());
    }

    public function testRecordsOfRestrictedModuleBlockedForNonMtcUser()
    {
        $category = $this->makeCategory(true);
        $property = Property::factory()->create(['category_id' => $category->id]);
        $user = $this->nonMtcUser();

        $this->actingAs($user)->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.properties.index', $category->slug))
            ->assertStatus(403);

        $this->actingAs($user)->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.properties.store', $category->slug), ['name' => 'foo'])
            ->assertStatus(403);

        $this->actingAs($user)->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.properties.show', [$category->slug, $property]))
            ->assertStatus(403);

        $this->actingAs($user)->withHeader('X-Tenant', tenant('id'))
            ->deleteJson(route('tenant.properties.destroy', [$category->slug, $property]))
            ->assertStatus(403);
    }

    public function testRecordsOfUnrestrictedModuleAccessibleForNonMtcUser()
    {
        $category = $this->makeCategory(false);

        $response = $this->actingAs($this->nonMtcUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.properties.index', $category->slug));

        $response->assertStatus(200);
    }

    public function testMtcUpdateCanSetRestriction()
    {
        $category = $this->makeCategory(false);

        $response = $this->actingAs($this->getUser('mtc'))
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.property-categories.update', $category), [
                'name' => 'baz',
                'active' => true,
                'superadmin_only' => true,
            ]);

        $response->assertStatus(200);
        $category->refresh();
        $this->assertTrue($category->superadmin_only);
    }

    public function testVisibilityScopeHidesRestrictedModuleFromNonMtcUser()
    {
        PropertyCategory::query()->delete();
        $this->makeCategory(false);
        $this->makeCategory(true);

        $visible = (new PropertyCategoryVisibilityScope())
            ->apply(PropertyCategory::query(), $this->nonMtcUser())
            ->count();

        $this->assertSame(1, $visible);
    }

    public function testVisibilityScopeShowsAllModulesToMtcUser()
    {
        PropertyCategory::query()->delete();
        $this->makeCategory(false);
        $this->makeCategory(true);

        $visible = (new PropertyCategoryVisibilityScope())
            ->apply(PropertyCategory::query(), $this->getUser('mtc'))
            ->count();

        $this->assertSame(2, $visible);
    }

    public function testMiddlewareStripsSuperadminOnlyForNonMtcWrite()
    {
        $request = $this->writeRequest($this->nonMtcUser());

        (new EnforcePropertyCategoryRestriction())
            ->handle($request, fn () => response('ok'));

        $this->assertFalse($request->boolean('superadmin_only'));
    }

    public function testMiddlewareKeepsSuperadminOnlyForMtcWrite()
    {
        $request = $this->writeRequest($this->getUser('mtc'));

        (new EnforcePropertyCategoryRestriction())
            ->handle($request, fn () => response('ok'));

        $this->assertTrue($request->boolean('superadmin_only'));
    }
}
