<?php

namespace Tests\Feature;

use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use Tests\TestCase;

class AuthorizationRoleCheckAuditTest extends TestCase
{
    private string $sourceDirectory;

    protected function setUp(): void
    {
        parent::setUp();

        $this->sourceDirectory = storage_path('framework/testing/authorization-role-audit-source');
        File::deleteDirectory($this->sourceDirectory);
        File::ensureDirectoryExists($this->sourceDirectory);

        config()->set('authorization_role_check_audit.source_scan_paths', [$this->sourceDirectory]);
        config()->set('authorization_role_check_audit.approved_role_check_paths', []);
        config()->set('authorization_role_check_audit.structural_role_reference_paths', []);
    }

    protected function tearDown(): void
    {
        File::deleteDirectory($this->sourceDirectory);

        parent::tearDown();
    }

    public function test_strict_audit_fails_for_unapproved_direct_role_authorization(): void
    {
        $source = $this->sourceDirectory.'/LegacyAccess.php';
        File::put($source, "<?php\n\nif (\$user->hasRole('branch-manager')) {\n    return true;\n}\n");

        $reportPath = storage_path('framework/testing/authorization-role-audit-fail.json');
        File::delete($reportPath);

        try {
            $exitCode = Artisan::call('authorization:audit-role-checks', [
                '--strict' => true,
                '--output' => $reportPath,
            ]);

            $this->assertSame(1, $exitCode, Artisan::output());
            $report = json_decode(File::get($reportPath), true, flags: JSON_THROW_ON_ERROR);

            $this->assertSame('fail', $report['status']);
            $this->assertSame(1, $report['summary']['authorization_checks']);
            $this->assertSame(1, $report['summary']['unapproved_authorization_checks']);
            $this->assertSame('has_role', $report['findings']['unapproved_authorization_checks'][0]['kind']);
        } finally {
            File::delete($reportPath);
        }
    }

    public function test_documented_role_check_exception_is_visible_but_does_not_fail_strict_audit(): void
    {
        $source = $this->sourceDirectory.'/TemporaryBridge.php';
        File::put($source, "<?php\n\nreturn \$user->hasAnyRole(['branch-manager', 'cashier']);\n");

        config()->set('authorization_role_check_audit.approved_role_check_paths', [
            str_replace('\\\\', '/', $source) => 'Temporary compatibility bridge covered by Phase 9 cleanup.',
        ]);

        $reportPath = storage_path('framework/testing/authorization-role-audit-approved.json');
        File::delete($reportPath);

        try {
            $exitCode = Artisan::call('authorization:audit-role-checks', [
                '--strict' => true,
                '--output' => $reportPath,
            ]);

            $this->assertSame(0, $exitCode, Artisan::output());
            $report = json_decode(File::get($reportPath), true, flags: JSON_THROW_ON_ERROR);

            $this->assertSame('pass', $report['status']);
            $this->assertSame(1, $report['summary']['authorization_checks']);
            $this->assertSame(0, $report['summary']['unapproved_authorization_checks']);
            $this->assertTrue($report['call_sites'][0]['approved']);
            $this->assertNotEmpty($report['call_sites'][0]['approval_reason']);
        } finally {
            File::delete($reportPath);
        }
    }

    public function test_role_queries_are_classified_as_structural_references_not_authorization_checks(): void
    {
        $source = $this->sourceDirectory.'/RoleAssignment.php';
        File::put($source, "<?php\n\n\$role = Role::where('name', 'cashier')->first();\n");

        $reportPath = storage_path('framework/testing/authorization-role-audit-structural.json');
        File::delete($reportPath);

        try {
            $exitCode = Artisan::call('authorization:audit-role-checks', [
                '--strict' => true,
                '--output' => $reportPath,
            ]);

            $this->assertSame(0, $exitCode, Artisan::output());
            $report = json_decode(File::get($reportPath), true, flags: JSON_THROW_ON_ERROR);

            $this->assertSame(0, $report['summary']['authorization_checks']);
            $this->assertSame(1, $report['summary']['structural_role_references']);
            $this->assertSame(1, $report['summary']['structural_references_requiring_review']);
            $this->assertSame('role_model_name_query', $report['call_sites'][0]['kind']);
        } finally {
            File::delete($reportPath);
        }
    }
}
