<?php

namespace Tests\Feature\Security;

use App\Models\Company;
use App\Models\Leave;
use App\Models\LeaveType;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Laravel\Sanctum\Sanctum;
use PHPUnit\Framework\Attributes\Group;
use Tests\TestCase;

/**
 * DB-backed security regressions. Requires mysql client for schema dump load.
 * See SecurityStaticChecksTest for local checks without DB.
 */
#[Group('database')]
class ProductionSecurityHardeningTest extends TestCase
{
    use RefreshDatabase;

    private function makeCompany(array $attrs = []): Company
    {
        return Company::query()->create(array_merge([
            'name' => 'Sec Co',
            'short_name' => 'SEC',
            'email' => 'sec-'.uniqid().'@example.test',
            'status' => 'active',
        ], $attrs));
    }

    private function makeStaff(Company $company, array $attrs = [], array $permissionNames = []): User
    {
        $role = Role::withoutCompanyScope()->create([
            'name' => 'employee-'.uniqid(),
            'display_name' => 'Employee',
            'company_id' => $company->id,
        ]);

        if ($permissionNames !== []) {
            $ids = [];
            foreach ($permissionNames as $name) {
                $ids[] = Permission::firstOrCreate(
                    ['name' => $name],
                    ['display_name' => $name, 'module' => 'security']
                )->id;
            }
            $role->permissions()->sync($ids);
        }

        return User::factory()->create(array_merge([
            'company_id' => $company->id,
            'role_id' => $role->id,
            'user_type' => User::USER_TYPE_STAFF,
            'allow_login' => true,
            'is_superadmin' => false,
            'status' => 'active',
            'password' => 'password',
        ], $attrs));
    }

    public function test_unprivileged_tenant_user_cannot_create_employee_via_api(): void
    {
        $company = $this->makeCompany();
        $user = $this->makeStaff($company, [], ['dashboard_view']);

        Sanctum::actingAs($user);

        $this->postJson('/api/employees', [
            'name' => 'New Hire',
            'email' => 'newhire@example.test',
            'password' => 'password123',
        ])->assertForbidden();
    }

    public function test_leave_show_denied_for_non_visible_peer(): void
    {
        $company = $this->makeCompany();
        $owner = $this->makeStaff($company, ['email' => 'owner@example.test'], ['leaves_view', 'leaves_create']);
        $peer = $this->makeStaff($company, ['email' => 'peer@example.test'], ['leaves_view']);

        $leaveType = LeaveType::query()->create([
            'company_id' => $company->id,
            'name' => 'Casual',
            'is_paid' => true,
        ]);

        $leave = Leave::query()->create([
            'company_id' => $company->id,
            'user_id' => $owner->id,
            'leave_type_id' => $leaveType->id,
            'start_date' => now()->toDateString(),
            'end_date' => now()->toDateString(),
            'total_days' => 1,
            'reason' => 'Personal',
            'status' => 'pending',
        ]);

        Sanctum::actingAs($peer);

        $this->getJson('/api/leaves/'.$leave->id)->assertForbidden();
    }

    public function test_user_json_hides_two_factor_secret_and_bank_pii(): void
    {
        $company = $this->makeCompany();
        $user = $this->makeStaff($company, [], ['dashboard_view']);
        $user->forceFill([
            'two_factor_secret' => 'SENSITIVE-SECRET',
            'account_number' => '1234567890',
            'pan_number' => 'ABCDE1234F',
            'ifsc_code' => 'HDFC0001234',
        ])->save();

        Sanctum::actingAs($user);

        $response = $this->getJson('/api/auth/user');
        $response->assertOk();
        $json = $response->json();
        $this->assertArrayNotHasKey('two_factor_secret', $json);
        $this->assertArrayNotHasKey('account_number', $json);
        $this->assertArrayNotHasKey('pan_number', $json);
        $this->assertArrayNotHasKey('ifsc_code', $json);
    }

    public function test_employee_document_upload_rejects_php_executable(): void
    {
        Storage::fake('local');
        $company = $this->makeCompany();
        $admin = $this->makeStaff($company, [
            'user_type' => User::USER_TYPE_ADMIN,
        ], ['documents_create', 'users_view', 'users_edit']);

        $employee = $this->makeStaff($company, ['email' => 'empdoc@example.test'], ['dashboard_view']);

        $category = array_key_first(config('employee_documents.upload_categories', ['id_proof' => 'ID Proof'])) ?: 'id_proof';

        $this->actingAs($admin)
            ->post(route('employees.documents.store', $employee->id), [
                'type' => $category,
                'document' => UploadedFile::fake()->create('shell.php', 10, 'application/x-php'),
            ])
            ->assertSessionHasErrors('document');
    }
}
