<?php

declare(strict_types=1);

use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\ActionSurfaceDiscoveredComponent;
use App\Support\Ui\ActionSurface\ActionSurfaceExemptions;
use App\Support\Ui\ActionSurface\ActionSurfaceProfileDefinition;
use App\Support\Ui\ActionSurface\ActionSurfaceSlotRequirement;
use App\Support\Ui\ActionSurface\ActionSurfaceValidator;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceComponentType;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfacePanelScope;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;

final class ActionSurfaceValidatorCompleteStub
{
    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::CrudListAndView)
            ->satisfy(ActionSurfaceSlot::ListHeader)
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ViewAction->value)
            ->satisfy(ActionSurfaceSlot::ListRowMoreMenu)
            ->satisfy(ActionSurfaceSlot::ListBulkMoreGroup)
            ->satisfy(ActionSurfaceSlot::ListEmptyState)
            ->satisfy(ActionSurfaceSlot::DetailHeader);
    }
}

final class ActionSurfaceValidatorMissingSlotStub
{
    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::CrudListAndView)
            ->satisfy(ActionSurfaceSlot::ListHeader)
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ViewAction->value);
    }
}

final class ActionSurfaceValidatorRunLogNoExportStub
{
    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::RunLog)
            ->withDefaults(new \App\Support\Ui\ActionSurface\ActionSurfaceDefaults(
                moreGroupLabel: 'More',
                exportIsDefaultBulkActionForReadOnly: false,
            ))
            ->satisfy(ActionSurfaceSlot::ListHeader)
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ViewAction->value)
            ->satisfy(ActionSurfaceSlot::ListBulkMoreGroup)
            ->satisfy(ActionSurfaceSlot::ListEmptyState)
            ->satisfy(ActionSurfaceSlot::DetailHeader);
    }
}

final class ActionSurfaceValidatorExemptSlotWithoutReasonStub
{
    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forResource(ActionSurfaceProfile::CrudListAndView)
            ->setSlot(ActionSurfaceSlot::ListHeader, ActionSurfaceSlotRequirement::exempt())
            ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ViewAction->value)
            ->satisfy(ActionSurfaceSlot::ListRowMoreMenu)
            ->satisfy(ActionSurfaceSlot::ListBulkMoreGroup)
            ->satisfy(ActionSurfaceSlot::ListEmptyState)
            ->satisfy(ActionSurfaceSlot::DetailHeader);
    }
}

final class ActionSurfaceValidatorNoDeclarationStub {}

function actionSurfaceComponent(string $className): ActionSurfaceDiscoveredComponent
{
    return new ActionSurfaceDiscoveredComponent(
        className: $className,
        componentType: ActionSurfaceComponentType::Resource,
        panelScopes: [ActionSurfacePanelScope::Tenant],
    );
}

it('passes when all required slots are declared', function (): void {
    $validator = new ActionSurfaceValidator(
        profileDefinition: new ActionSurfaceProfileDefinition,
        exemptions: new ActionSurfaceExemptions([]),
    );

    $result = $validator->validateComponents([actionSurfaceComponent(ActionSurfaceValidatorCompleteStub::class)]);

    expect($result->hasIssues())->toBeFalse($result->formatForAssertion());
});

it('fails when a required slot is missing', function (): void {
    $validator = new ActionSurfaceValidator(
        profileDefinition: new ActionSurfaceProfileDefinition,
        exemptions: new ActionSurfaceExemptions([]),
    );

    $result = $validator->validateComponents([actionSurfaceComponent(ActionSurfaceValidatorMissingSlotStub::class)]);

    expect($result->hasIssues())->toBeTrue();
    expect($result->formatForAssertion())->toContain('Required slot is not declared');
});

it('fails missing declarations when no baseline exemption exists', function (): void {
    $validator = new ActionSurfaceValidator(
        profileDefinition: new ActionSurfaceProfileDefinition,
        exemptions: new ActionSurfaceExemptions([]),
    );

    $result = $validator->validateComponents([actionSurfaceComponent(ActionSurfaceValidatorNoDeclarationStub::class)]);

    expect($result->hasIssues())->toBeTrue();
    expect($result->formatForAssertion())->toContain('Missing action-surface declaration');
});

it('accepts missing declarations when explicit baseline exemption exists', function (): void {
    $validator = new ActionSurfaceValidator(
        profileDefinition: new ActionSurfaceProfileDefinition,
        exemptions: new ActionSurfaceExemptions([
            ActionSurfaceValidatorNoDeclarationStub::class => 'Retrofit intentionally deferred in validator unit test.',
        ]),
    );

    $result = $validator->validateComponents([actionSurfaceComponent(ActionSurfaceValidatorNoDeclarationStub::class)]);

    expect($result->hasIssues())->toBeFalse($result->formatForAssertion());
});

it('requires a bulk exemption reason when run-log export default is disabled', function (): void {
    $validator = new ActionSurfaceValidator(
        profileDefinition: new ActionSurfaceProfileDefinition,
        exemptions: new ActionSurfaceExemptions([]),
    );

    $result = $validator->validateComponents([actionSurfaceComponent(ActionSurfaceValidatorRunLogNoExportStub::class)]);

    expect($result->hasIssues())->toBeTrue();
    expect($result->formatForAssertion())->toContain('ReadOnly/RunLog profile disables Export default');
});

it('fails when slot exemption reason is missing', function (): void {
    $validator = new ActionSurfaceValidator(
        profileDefinition: new ActionSurfaceProfileDefinition,
        exemptions: new ActionSurfaceExemptions([]),
    );

    $result = $validator->validateComponents([actionSurfaceComponent(ActionSurfaceValidatorExemptSlotWithoutReasonStub::class)]);

    expect($result->hasIssues())->toBeTrue();
    expect($result->formatForAssertion())->toContain('Slot is marked exempt but exemption reason is missing or empty');
});
