<?php

namespace Tests\Feature;

use App\Auth\Permissions;
use App\Models\Central\Company;
use App\Models\Central\Plan;
use App\Models\Tenant\User;
use App\Services\RbacSeeder;
use App\Tenancy\TenantManager;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Spatie\Permission\PermissionRegistrar;
use Tests\TestCase;

class RbacTest extends TestCase
{
    use RefreshDatabase;

    private Company $company;

    protected function setUp(): void
    {
        parent::setUp();
        $plan = Plan::firstOrCreate(['slug' => 'test'], ['name' => 'T', 'price' => 0, 'features' => []]);
        $this->company = Company::create([
            'plan_id' => $plan->id, 'slug' => 'rbac-' . uniqid(),
            'name' => 'RBAC Co', 'status' => 'active',
        ]);
        app(TenantManager::class)->setCurrent($this->company);
        app(RbacSeeder::class)->seedTenantRoles($this->company->id);
        app(PermissionRegistrar::class)->setPermissionsTeamId($this->company->id);
    }

    public function test_seeder_creates_51_permissions(): void
    {
        $this->assertEquals(52, Permission::where('guard_name', 'web')->count());
    }

    public function test_seeder_creates_7_roles_per_tenant(): void
    {
        $roles = Role::where('company_id', $this->company->id)->pluck('name')->toArray();
        sort($roles);
        $this->assertEquals(['accountant', 'admin', 'manager', 'mechanic', 'owner', 'receptionist', 'viewer'], $roles);
    }

    public function test_owner_role_has_all_permissions(): void
    {
        $owner = Role::where('company_id', $this->company->id)->where('name', 'owner')->first();
        $this->assertEquals(52, $owner->permissions->count());
    }

    public function test_mechanic_role_has_minimal_permissions(): void
    {
        $mechanic = Role::where('company_id', $this->company->id)->where('name', 'mechanic')->first();
        $perms = $mechanic->permissions->pluck('name')->toArray();
        $this->assertContains(Permissions::WORK_ORDERS_VIEW_OWN_ASSIGNED, $perms);
        $this->assertContains(Permissions::INVENTORY_VIEW, $perms);
        $this->assertNotContains(Permissions::WORK_ORDERS_VIEW_ALL, $perms);
        $this->assertNotContains(Permissions::FINANCE_VIEW_OVERVIEW, $perms);
        $this->assertNotContains(Permissions::ADMIN_USERS_MANAGE, $perms);
    }

    public function test_accountant_can_see_finance_but_not_admin(): void
    {
        $accountant = Role::where('company_id', $this->company->id)->where('name', 'accountant')->first();
        $perms = $accountant->permissions->pluck('name')->toArray();
        $this->assertContains(Permissions::FINANCE_VIEW_OVERVIEW, $perms);
        $this->assertContains(Permissions::FINANCE_VIEW_PL, $perms);
        $this->assertContains(Permissions::SALARIES_CALCULATE, $perms);
        $this->assertNotContains(Permissions::ADMIN_USERS_MANAGE, $perms);
        $this->assertNotContains(Permissions::WORK_ORDERS_DELETE, $perms);
    }

    public function test_user_can_method_returns_true_when_role_has_permission(): void
    {
        $user = User::create(['name' => 'M', 'email' => 'm-' . uniqid() . '@e.com', 'password' => bcrypt('x'), 'role' => 'mechanic', 'status' => 'active']);
        $user->syncRoles(['mechanic']);

        $this->assertTrue($user->canDo(Permissions::WORK_ORDERS_VIEW_OWN_ASSIGNED));
        $this->assertFalse($user->canDo(Permissions::FINANCE_VIEW_OVERVIEW));
    }

    public function test_admin_bypasses_permission_check(): void
    {
        $admin = User::create(['name' => 'A', 'email' => 'a-' . uniqid() . '@e.com', 'password' => bcrypt('x'), 'role' => 'admin', 'status' => 'active']);
        $admin->syncRoles(['admin']);

        // Admin gets the bypass even if a permission is not explicitly granted
        $this->assertTrue($admin->canDo('some.permission.that.does.not.exist'));
        $this->assertTrue($admin->canDo(Permissions::FINANCE_DELETE_PAYMENT));
    }

    public function test_owner_helper_returns_true_for_owner_role_user(): void
    {
        $user = User::create(['name' => 'O', 'email' => 'o-' . uniqid() . '@e.com', 'password' => bcrypt('x'), 'role' => 'owner', 'status' => 'active']);
        $this->assertTrue($user->isOwner());
        $this->assertTrue($user->isAdmin());  // owner counts as admin for canDo bypass
    }

    public function test_sync_users_to_roles_maps_legacy_role_strings(): void
    {
        $u1 = User::create(['name' => 'X', 'email' => 'x@e.com', 'password' => bcrypt('x'), 'role' => 'parts_manager', 'status' => 'active']);
        $u2 = User::create(['name' => 'Y', 'email' => 'y@e.com', 'password' => bcrypt('x'), 'role' => 'master', 'status' => 'active']);
        $u3 = User::create(['name' => 'Z', 'email' => 'z@e.com', 'password' => bcrypt('x'), 'role' => 'user', 'status' => 'active']);

        app(RbacSeeder::class)->syncUsersToRoles($this->company->id);

        $u1->refresh(); $u2->refresh(); $u3->refresh();
        // parts_manager → manager
        $this->assertTrue($u1->hasRole('manager'));
        // master → mechanic
        $this->assertTrue($u2->hasRole('mechanic'));
        // user → viewer
        $this->assertTrue($u3->hasRole('viewer'));
    }

    public function test_two_factor_helper_reflects_app_authentication_secret(): void
    {
        $user = User::create(['name' => 'T', 'email' => 't@e.com', 'password' => bcrypt('x'), 'role' => 'admin', 'status' => 'active']);
        $this->assertFalse($user->hasTwoFactorEnabled());

        $user->saveAppAuthenticationSecret('FAKEBASE32SECRET====');
        $user->refresh();
        $this->assertTrue($user->hasTwoFactorEnabled());

        $user->saveAppAuthenticationSecret(null);
        $user->refresh();
        $this->assertFalse($user->hasTwoFactorEnabled());
    }
}
