<?php

use App\Support\Baselines\BaselineScope;

it('expands empty policy_types to supported policy types (excluding foundations) and defaults foundation_types to none', function () {
    config()->set('tenantpilot.supported_policy_types', [
        ['type' => 'deviceConfiguration', 'label' => 'Device Configuration'],
        ['type' => 'deviceCompliancePolicy', 'label' => 'Device Compliance'],
    ]);

    config()->set('tenantpilot.foundation_types', [
        ['type' => 'assignmentFilter', 'label' => 'Assignment Filter', 'baseline_compare' => ['supported' => true]],
    ]);

    $scope = BaselineScope::fromJsonb([
        'policy_types' => [],
        'foundation_types' => [],
    ])->expandDefaults();

    expect($scope->policyTypes)->toBe([
        'deviceCompliancePolicy',
        'deviceConfiguration',
    ]);

    expect($scope->foundationTypes)->toBe([]);
    expect($scope->allTypes())->toBe([
        'deviceCompliancePolicy',
        'deviceConfiguration',
    ]);
});

it('filters unknown types and does not allow foundations inside policy_types', function () {
    config()->set('tenantpilot.supported_policy_types', [
        ['type' => 'deviceConfiguration'],
    ]);

    config()->set('tenantpilot.foundation_types', [
        ['type' => 'assignmentFilter', 'baseline_compare' => ['supported' => true]],
    ]);

    $scope = BaselineScope::fromJsonb([
        'policy_types' => ['deviceConfiguration', 'assignmentFilter', 'unknown'],
        'foundation_types' => ['assignmentFilter', 'unknown'],
    ])->expandDefaults();

    expect($scope->policyTypes)->toBe(['deviceConfiguration']);
    expect($scope->foundationTypes)->toBe(['assignmentFilter']);
    expect($scope->allTypes())->toBe(['assignmentFilter', 'deviceConfiguration']);
});

it('normalizes canonical v2 entries and preserves canonical storage', function (): void {
    config()->set('tenantpilot.supported_policy_types', [
        ['type' => 'deviceConfiguration', 'label' => 'Device Configuration'],
        ['type' => 'deviceCompliancePolicy', 'label' => 'Device Compliance'],
    ]);

    config()->set('tenantpilot.foundation_types', [
        ['type' => 'assignmentFilter', 'label' => 'Assignment Filter', 'baseline_compare' => ['supported' => true]],
    ]);

    $scope = BaselineScope::fromJsonb([
        'version' => 2,
        'entries' => [
            [
                'domain_key' => 'intune',
                'subject_class' => 'policy',
                'subject_type_keys' => ['deviceConfiguration', 'deviceCompliancePolicy', 'deviceConfiguration'],
                'filters' => [],
            ],
            [
                'domain_key' => 'intune',
                'subject_class' => 'policy',
                'subject_type_keys' => ['deviceCompliancePolicy'],
                'filters' => [],
            ],
            [
                'domain_key' => 'platform_foundation',
                'subject_class' => 'configuration_resource',
                'subject_type_keys' => ['assignmentFilter'],
                'filters' => [],
            ],
        ],
    ]);

    expect($scope->policyTypes)->toBe(['deviceCompliancePolicy', 'deviceConfiguration'])
        ->and($scope->foundationTypes)->toBe(['assignmentFilter'])
        ->and($scope->toStoredJsonb())->toBe([
            'version' => 2,
            'entries' => [
                [
                    'domain_key' => 'intune',
                    'subject_class' => 'policy',
                    'subject_type_keys' => ['deviceCompliancePolicy', 'deviceConfiguration'],
                    'filters' => [],
                ],
                [
                    'domain_key' => 'platform_foundation',
                    'subject_class' => 'configuration_resource',
                    'subject_type_keys' => ['assignmentFilter'],
                    'filters' => [],
                ],
            ],
        ])
        ->and($scope->normalizationLineage())->toMatchArray([
            'source_shape' => 'canonical_v2',
            'normalized_on_read' => false,
            'save_forward_required' => false,
        ]);
});

it('treats a missing legacy bucket like its empty default when the other bucket is present', function (): void {
    config()->set('tenantpilot.supported_policy_types', [
        ['type' => 'deviceConfiguration'],
        ['type' => 'deviceCompliancePolicy'],
    ]);

    config()->set('tenantpilot.foundation_types', [
        ['type' => 'assignmentFilter', 'baseline_compare' => ['supported' => true]],
    ]);

    $policyOnly = BaselineScope::fromJsonb([
        'policy_types' => ['deviceConfiguration'],
    ]);
    $foundationOnly = BaselineScope::fromJsonb([
        'foundation_types' => ['assignmentFilter'],
    ]);

    expect($policyOnly->policyTypes)->toBe(['deviceConfiguration'])
        ->and($policyOnly->foundationTypes)->toBe([])
        ->and($foundationOnly->policyTypes)->toBe(['deviceCompliancePolicy', 'deviceConfiguration'])
        ->and($foundationOnly->foundationTypes)->toBe(['assignmentFilter']);
});

it('rejects mixed legacy and canonical payloads', function (): void {
    expect(fn () => BaselineScope::fromJsonb([
        'policy_types' => ['deviceConfiguration'],
        'version' => 2,
        'entries' => [
            [
                'domain_key' => 'intune',
                'subject_class' => 'policy',
                'subject_type_keys' => ['deviceConfiguration'],
            ],
        ],
    ]))->toThrow(InvalidArgumentException::class, 'must not mix legacy buckets');
});

it('rejects unsupported filters for current domains', function (): void {
    config()->set('tenantpilot.supported_policy_types', [
        ['type' => 'deviceConfiguration'],
    ]);

    expect(fn () => BaselineScope::fromJsonb([
        'version' => 2,
        'entries' => [
            [
                'domain_key' => 'intune',
                'subject_class' => 'policy',
                'subject_type_keys' => ['deviceConfiguration'],
                'filters' => ['tenant_ids' => ['tenant-a']],
            ],
        ],
    ]))->toThrow(InvalidArgumentException::class, 'Filters are not supported');
});

it('treats empty legacy override payloads as no override when requested', function (): void {
    $scope = BaselineScope::fromJsonb([
        'policy_types' => [],
        'foundation_types' => [],
    ], allowEmptyLegacyAsNoOverride: true);

    expect($scope->isEmpty())->toBeTrue();
});

it('rejects unknown governance domains', function (): void {
    expect(fn () => BaselineScope::fromJsonb([
        'version' => 2,
        'entries' => [
            [
                'domain_key' => 'unknown_domain',
                'subject_class' => 'policy',
                'subject_type_keys' => ['deviceConfiguration'],
                'filters' => [],
            ],
        ],
    ]))->toThrow(InvalidArgumentException::class, 'Unknown governance domain');
});

it('rejects invalid subject classes for known domains', function (): void {
    expect(fn () => BaselineScope::fromJsonb([
        'version' => 2,
        'entries' => [
            [
                'domain_key' => 'intune',
                'subject_class' => 'configuration_resource',
                'subject_type_keys' => ['deviceConfiguration'],
                'filters' => [],
            ],
        ],
    ]))->toThrow(InvalidArgumentException::class, 'is not valid for domain');
});

it('rejects inactive subject types in canonical scope entries', function (): void {
    expect(fn () => BaselineScope::fromJsonb([
        'version' => 2,
        'entries' => [
            [
                'domain_key' => 'platform_foundation',
                'subject_class' => 'configuration_resource',
                'subject_type_keys' => ['intuneRoleAssignment'],
                'filters' => [],
            ],
        ],
    ]))->toThrow(InvalidArgumentException::class, 'Inactive subject type');
});

it('rejects future-domain selections that have no active subject type mapping yet', function (): void {
    expect(fn () => BaselineScope::fromJsonb([
        'version' => 2,
        'entries' => [
            [
                'domain_key' => 'entra',
                'subject_class' => 'control',
                'subject_type_keys' => ['conditionalAccessPolicy'],
                'filters' => [],
            ],
        ],
    ]))->toThrow(InvalidArgumentException::class, 'Unknown subject type');
});
