<?php

declare(strict_types=1);

use App\Models\StoredReport;
use App\Models\ManagedEnvironment;
use App\Services\PermissionPosture\FindingGeneratorContract;
use App\Services\PermissionPosture\PostureScoreCalculator;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('creates a stored report with factory defaults', function (): void {
    $report = StoredReport::factory()->create();

    expect($report)->toBeInstanceOf(StoredReport::class)
        ->and($report->report_type)->toBe(StoredReport::REPORT_TYPE_PERMISSION_POSTURE)
        ->and($report->payload)->toBeArray()
        ->and($report->payload)->toHaveKeys(['posture_score', 'required_count', 'granted_count', 'checked_at', 'permissions']);
});

it('casts payload to array', function (): void {
    $report = StoredReport::factory()->create();

    $fresh = StoredReport::query()->find($report->getKey());

    expect($fresh->payload)->toBeArray()
        ->and($fresh->payload['posture_score'])->toBe(86);
});

it('belongs to a tenant', function (): void {
    $report = StoredReport::factory()->create();

    expect($report->tenant)->toBeInstanceOf(ManagedEnvironment::class)
        ->and($report->tenant->getKey())->toBe($report->managed_environment_id);
});

it('belongs to a workspace via DerivesWorkspaceIdFromTenant', function (): void {
    [$user, $tenant] = createUserWithTenant();

    $report = StoredReport::factory()->create([
        'managed_environment_id' => $tenant->getKey(),
    ]);

    expect($report->workspace_id)->toBe($tenant->workspace_id);
});

it('has the correct REPORT_TYPE_PERMISSION_POSTURE constant', function (): void {
    expect(StoredReport::REPORT_TYPE_PERMISSION_POSTURE)->toBe('permission_posture');
});

// --- T024: Generator integration scenarios ---

it('generator creates report with correct report_type and payload schema', function (): void {
    [$user, $tenant] = createUserWithTenant();

    $generator = app(FindingGeneratorContract::class);

    $comparison = [
        'overall_status' => 'missing',
        'permissions' => [
            ['key' => 'Perm.A', 'type' => 'application', 'status' => 'granted', 'features' => ['backup']],
            ['key' => 'Perm.B', 'type' => 'application', 'status' => 'missing', 'features' => ['restore']],
            ['key' => 'Perm.C', 'type' => 'delegated', 'status' => 'granted', 'features' => ['sync']],
        ],
        'last_refreshed_at' => now()->toIso8601String(),
    ];

    $generator->generate($tenant, $comparison);

    $report = StoredReport::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('report_type', StoredReport::REPORT_TYPE_PERMISSION_POSTURE)
        ->first();

    expect($report)->not->toBeNull()
        ->and($report->report_type)->toBe(StoredReport::REPORT_TYPE_PERMISSION_POSTURE)
        ->and($report->payload)->toHaveKeys(['posture_score', 'required_count', 'granted_count', 'checked_at', 'permissions'])
        ->and($report->payload['required_count'])->toBe(3)
        ->and($report->payload['granted_count'])->toBe(2)
        ->and($report->payload['permissions'])->toHaveCount(3);
});

it('generator report posture_score matches PostureScoreCalculator output', function (): void {
    [$user, $tenant] = createUserWithTenant();

    $comparison = [
        'overall_status' => 'missing',
        'permissions' => [
            ['key' => 'Perm.A', 'type' => 'application', 'status' => 'granted', 'features' => []],
            ['key' => 'Perm.B', 'type' => 'application', 'status' => 'missing', 'features' => []],
            ['key' => 'Perm.C', 'type' => 'application', 'status' => 'missing', 'features' => []],
        ],
        'last_refreshed_at' => now()->toIso8601String(),
    ];

    $expectedScore = (new PostureScoreCalculator)->calculate($comparison);

    $generator = app(FindingGeneratorContract::class);
    $result = $generator->generate($tenant, $comparison);

    $report = StoredReport::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('report_type', StoredReport::REPORT_TYPE_PERMISSION_POSTURE)
        ->first();

    expect($report->payload['posture_score'])->toBe($expectedScore)
        ->and($result->postureScore)->toBe($expectedScore)
        ->and($expectedScore)->toBe(33);
});

// --- T025: Temporal ordering ---

it('multiple posture reports for same tenant ordered by created_at descending', function (): void {
    [$user, $tenant] = createUserWithTenant();

    // Create reports at different timestamps
    $first = StoredReport::factory()->create([
        'managed_environment_id' => $tenant->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
        'created_at' => now()->subHours(3),
    ]);

    $second = StoredReport::factory()->create([
        'managed_environment_id' => $tenant->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
        'created_at' => now()->subHours(1),
    ]);

    $third = StoredReport::factory()->create([
        'managed_environment_id' => $tenant->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
        'created_at' => now(),
    ]);

    $results = StoredReport::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('report_type', StoredReport::REPORT_TYPE_PERMISSION_POSTURE)
        ->orderByDesc('created_at')
        ->pluck('id');

    expect($results->count())->toBe(3)
        ->and($results[0])->toBe($third->getKey())
        ->and($results[1])->toBe($second->getKey())
        ->and($results[2])->toBe($first->getKey());
});

it('reports queryable by managed_environment_id and report_type', function (): void {
    [$user, $tenantA] = createUserWithTenant();
    [$user2, $tenantB] = createUserWithTenant();

    StoredReport::factory()->count(2)->create([
        'managed_environment_id' => $tenantA->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
    ]);

    StoredReport::factory()->create([
        'managed_environment_id' => $tenantB->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
    ]);

    $tenantAReports = StoredReport::query()
        ->where('managed_environment_id', $tenantA->getKey())
        ->where('report_type', StoredReport::REPORT_TYPE_PERMISSION_POSTURE)
        ->count();

    expect($tenantAReports)->toBe(2);
});

// --- T026: Polymorphic reusability ---

it('different report_type coexists with permission_posture reports', function (): void {
    [$user, $tenant] = createUserWithTenant();

    StoredReport::factory()->create([
        'managed_environment_id' => $tenant->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
    ]);

    StoredReport::create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'report_type' => 'compliance_summary',
        'payload' => ['compliant' => 5, 'noncompliant' => 2],
    ]);

    $postureReports = StoredReport::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('report_type', StoredReport::REPORT_TYPE_PERMISSION_POSTURE)
        ->count();

    $complianceReports = StoredReport::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->where('report_type', 'compliance_summary')
        ->count();

    $allReports = StoredReport::query()
        ->where('managed_environment_id', $tenant->getKey())
        ->count();

    expect($postureReports)->toBe(1)
        ->and($complianceReports)->toBe(1)
        ->and($allReports)->toBe(2);
});
