<?php

declare(strict_types=1);

use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Resources\FindingExceptionResource;
use App\Support\WorkspaceIsolation\TenantOwnedModelFamilies;
use App\Support\WorkspaceIsolation\TenantOwnedTables;

function tenantOwnedFamilySource(string $className): string
{
    $reflection = new ReflectionClass($className);
    $fileName = $reflection->getFileName();

    expect($fileName)->toBeString();

    $contents = file_get_contents((string) $fileName);

    expect($contents)->not->toBeFalse();

    return (string) $contents;
}

it('keeps the first-slice tenant-owned family inventory aligned to tenant-owned tables', function (): void {
    $allowedSearchPostures = ['scoped', 'disabled', 'not_applicable'];

    foreach (TenantOwnedModelFamilies::firstSlice() as $familyName => $family) {
        expect(TenantOwnedTables::contains($family['table']))
            ->toBeTrue("{$familyName} must point at a known tenant-owned table.");

        expect(TenantOwnedTables::firstSlice())
            ->toContain($family['table']);

        expect($allowedSearchPostures)
            ->toContain($family['search_posture']);

        expect(class_exists($family['model']))->toBeTrue();
        expect(class_exists($family['resource']))->toBeTrue();
    }
});

it('keeps first-slice family names unique and explicit', function (): void {
    $names = TenantOwnedModelFamilies::names();

    expect($names)->not->toBeEmpty();
    expect(array_unique($names))->toBe($names);
});

it('keeps the first-slice family registry exhaustive for declared first-slice tenant-owned tables', function (): void {
    $familyTables = array_map(
        static fn (array $family): string => $family['table'],
        TenantOwnedModelFamilies::firstSlice(),
    );

    expect($familyTables)->toEqualCanonicalizing(TenantOwnedTables::firstSlice());
});

it('keeps the residual tenant-owned rollout inventory aligned to non-first-slice tenant-owned tables', function (): void {
    $residualTables = array_map(
        static fn (array $entry): string => $entry['table'],
        TenantOwnedModelFamilies::residualRolloutInventory(),
    );

    expect($residualTables)->toEqualCanonicalizing(TenantOwnedTables::residual());

    foreach (TenantOwnedModelFamilies::residualRolloutInventory() as $familyName => $entry) {
        expect(trim($familyName))->not->toBe('');
        expect(trim($entry['likely_surface']))->not->toBe('');
        expect(trim($entry['why_not_in_first_slice']))->not->toBe('');
    }
});

it('requires first-slice resources to use the shared tenant-owned query and record resolver entry points', function (): void {
    foreach (TenantOwnedModelFamilies::firstSlice() as $familyName => $family) {
        $resourceClass = $family['resource'];
        $traits = class_uses_recursive($resourceClass);
        $source = tenantOwnedFamilySource($resourceClass);

        expect(in_array(InteractsWithTenantOwnedRecords::class, $traits, true))
            ->toBeTrue("{$familyName} must use the shared tenant-owned resource trait.");

        expect(preg_match('/public\s+static\s+function\s+getEloquentQuery\s*\(/', $source) === 1)
            ->toBeTrue("{$familyName} must expose an explicit scoped query entry point.");

        expect(preg_match('/static::(?:getTenantOwnedEloquentQuery|scopeTenantOwnedQuery)\s*\(/', $source) === 1)
            ->toBeTrue("{$familyName} must derive records from the canonical tenant-owned query helper.");

        expect(preg_match('/public\s+static\s+function\s+resolveScopedRecordOrFail\s*\(/', $source) === 1)
            ->toBeTrue("{$familyName} must expose an explicit scoped-record resolver.");

        expect(str_contains($source, 'resolveTenantOwnedRecordOrFail'))
            ->toBeTrue("{$familyName} must resolve detail records through the shared tenant-owned resolver.");
    }
});

it('documents explicit scope exceptions with non-empty reasons', function (): void {
    $exceptions = TenantOwnedModelFamilies::explicitScopeExceptions();
    $exceptionMetadata = TenantOwnedModelFamilies::scopeExceptions();

    expect($exceptions)->not->toBeEmpty();
    expect(array_keys($exceptionMetadata))->toEqual(array_keys($exceptions));

    foreach ($exceptions as $surfaceName => $reason) {
        expect($surfaceName)->not->toBe('');
        expect(trim($reason))->not->toBe('');
        expect($exceptionMetadata[$surfaceName]['exception_kind'])->not->toBe('');
        expect($exceptionMetadata[$surfaceName]['still_required_checks'])->not->toBeEmpty();
    }
});

it('keeps finding exception discovery tenant-scoped and global-search disabled', function (): void {
    $source = tenantOwnedFamilySource(FindingExceptionResource::class);

    expect(preg_match('/protected\s+static\s+bool\s+\$isGloballySearchable\s*=\s*false;/', $source) === 1)
        ->toBeTrue('FindingExceptionResource must keep global search disabled until a tenant-safe global search flow is introduced.');

    expect(preg_match('/getTenantOwnedEloquentQuery\s*\(\)\s*->with\(static::relationshipsForView\(\)\)/s', $source) === 1)
        ->toBeTrue('FindingExceptionResource must derive list queries from the canonical tenant-owned query helper and scoped relationship loader.');

    expect(preg_match('/resolveTenantOwnedRecordOrFail\s*\(\$record,\s*parent::getEloquentQuery\(\)->with\(static::relationshipsForView\(\)\)\)/s', $source) === 1)
        ->toBeTrue('FindingExceptionResource must resolve detail records through the shared tenant-owned resolver with the same scoped relationships.');
});
