<?php

declare(strict_types=1);

use App\Filament\Pages\CrossEnvironmentComparePage;
use App\Models\OperationRun;
use App\Models\OperationalControlActivation;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Auth\CapabilityResolver;
use App\Support\Auth\Capabilities;
use App\Support\PortfolioCompare\CrossEnvironmentComparePreviewBuilder;
use App\Support\PortfolioCompare\CrossEnvironmentCompareSelection;
use App\Support\PortfolioCompare\CrossEnvironmentPromotionPreflight;
use Filament\Actions\Action;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\Feature\Concerns\BuildsPortfolioCompareFixtures;

uses(RefreshDatabase::class, BuildsPortfolioCompareFixtures::class);

it('keeps execute promotion visible but disabled for compare-only actors without target manage access', function (): void {
    $fixture = $this->makeCrossEnvironmentCompareFixture(workspaceRole: 'owner', tenantRole: 'readonly');

    $this->createPortfolioCompareSubject(
        tenant: $fixture['sourceEnvironment'],
        displayName: 'Compare Only Policy',
        snapshot: ['settings' => [['key' => 'readonly', 'value' => 1]]],
    );

    $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);

    $query = [
        'source_environment_id' => (int) $fixture['sourceEnvironment']->getKey(),
        'target_environment_id' => (int) $fixture['targetEnvironment']->getKey(),
        'policy_type' => ['deviceConfiguration'],
    ];

    $component = Livewire::withQueryParams($query)
        ->actingAs($fixture['user'])
        ->test(CrossEnvironmentComparePage::class)
        ->call('generatePromotionPreflight');

    $realResolver = app(CapabilityResolver::class);
    $resolver = \Mockery::mock(CapabilityResolver::class);
    $resolver->shouldReceive('isMember')
        ->andReturnUsing(fn (User $user, ManagedEnvironment $tenant): bool => $realResolver->isMember($user, $tenant));
    $resolver->shouldReceive('can')
        ->andReturnUsing(function (User $user, ManagedEnvironment $tenant, string $capability) use ($fixture, $realResolver): bool {
            if ($tenant->is($fixture['targetEnvironment']) && $capability === Capabilities::TENANT_MANAGE) {
                return false;
            }

            return $realResolver->can($user, $tenant, $capability);
        });

    app()->instance(CapabilityResolver::class, $resolver);

    $component
        ->assertActionVisible('executePromotion')
        ->assertActionDisabled('executePromotion')
        ->assertActionExists('executePromotion', fn (Action $action): bool => $action->getTooltip() === 'You need target environment manage access to execute promotion.')
        ->call('executePromotion')
        ->assertForbidden();
});

it('keeps execute promotion visible but disabled without workspace baseline manage access and forbids forced execution', function (): void {
    $fixture = $this->makeCrossEnvironmentCompareFixture(workspaceRole: 'readonly', tenantRole: 'owner');

    $this->createPortfolioCompareSubject(
        tenant: $fixture['sourceEnvironment'],
        displayName: 'Workspace Gate Policy',
        snapshot: ['settings' => [['key' => 'workspace', 'value' => 1]]],
    );

    $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);

    $selection = new CrossEnvironmentCompareSelection(
        $fixture['sourceEnvironment'],
        $fixture['targetEnvironment'],
        ['deviceConfiguration'],
    );
    $preview = app(CrossEnvironmentComparePreviewBuilder::class)->build($selection);
    $preflight = app(CrossEnvironmentPromotionPreflight::class)->build($preview);

    Livewire::withQueryParams([
        'source_environment_id' => (int) $fixture['sourceEnvironment']->getKey(),
        'target_environment_id' => (int) $fixture['targetEnvironment']->getKey(),
        'policy_type' => ['deviceConfiguration'],
    ])
        ->actingAs($fixture['user'])
        ->test(CrossEnvironmentComparePage::class)
        ->set('preview', $preview)
        ->set('preflight', $preflight)
        ->assertActionVisible('executePromotion')
        ->assertActionDisabled('executePromotion')
        ->assertActionExists('executePromotion', fn (Action $action): bool => $action->getTooltip() === 'You need workspace baseline manage access to execute promotion.')
        ->call('executePromotion')
        ->assertForbidden();
});

it('does not queue a promotion run when the current preflight is stale for the selected target environment', function (): void {
    $fixture = $this->makeCrossEnvironmentCompareFixture();

    $this->createPortfolioCompareSubject(
        tenant: $fixture['sourceEnvironment'],
        displayName: 'Stale Policy',
        snapshot: ['settings' => [['key' => 'stale', 'value' => 1]]],
    );

    $staleTarget = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $fixture['workspace']->getKey(),
        'name' => 'Stale Target',
    ]);

    $fixture['user']->tenants()->syncWithoutDetaching([
        (int) $staleTarget->getKey() => ['role' => 'owner'],
    ]);
    app(CapabilityResolver::class)->clearCache();

    $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);

    $staleSelection = new CrossEnvironmentCompareSelection(
        $fixture['sourceEnvironment'],
        $staleTarget,
        ['deviceConfiguration'],
    );
    $currentSelection = new CrossEnvironmentCompareSelection(
        $fixture['sourceEnvironment'],
        $fixture['targetEnvironment'],
        ['deviceConfiguration'],
    );
    $currentPreview = app(CrossEnvironmentComparePreviewBuilder::class)->build($currentSelection);
    $stalePreview = app(CrossEnvironmentComparePreviewBuilder::class)->build($staleSelection);
    $stalePreflight = app(CrossEnvironmentPromotionPreflight::class)->build($stalePreview);

    Livewire::withQueryParams([
        'source_environment_id' => (int) $fixture['sourceEnvironment']->getKey(),
        'target_environment_id' => (int) $fixture['targetEnvironment']->getKey(),
        'policy_type' => ['deviceConfiguration'],
    ])
        ->actingAs($fixture['user'])
        ->test(CrossEnvironmentComparePage::class)
        ->set('preview', $currentPreview)
        ->set('preflight', $stalePreflight)
        ->call('executePromotion')
        ->assertNotified('Promotion execution unavailable');

    expect(OperationRun::query()->count())->toBe(0);
});

it('does not queue a promotion run when promotion execution is paused by operational control', function (): void {
    $fixture = $this->makeCrossEnvironmentCompareFixture();

    $this->createPortfolioCompareSubject(
        tenant: $fixture['sourceEnvironment'],
        displayName: 'Paused Policy',
        snapshot: ['settings' => [['key' => 'paused', 'value' => 1]]],
    );

    $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);

    OperationalControlActivation::factory()->workspaceScoped()->create([
        'control_key' => 'promotion.execute',
        'workspace_id' => (int) $fixture['workspace']->getKey(),
        'reason_text' => 'Paused during promotion review.',
    ]);

    Livewire::withQueryParams([
        'source_environment_id' => (int) $fixture['sourceEnvironment']->getKey(),
        'target_environment_id' => (int) $fixture['targetEnvironment']->getKey(),
        'policy_type' => ['deviceConfiguration'],
    ])
        ->actingAs($fixture['user'])
        ->test(CrossEnvironmentComparePage::class)
        ->call('generatePromotionPreflight')
        ->call('executePromotion')
        ->assertNotified('Promotion execution paused');

    expect(OperationRun::query()->count())->toBe(0);
});

it('returns 404 and does not queue a promotion run when the requested target environment is outside the actor scope', function (): void {
    $fixture = $this->makeCrossEnvironmentCompareFixture();
    $hiddenTarget = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $fixture['workspace']->getKey(),
        'name' => 'Hidden Promotion Target',
    ]);

    $session = $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);

    $this->withSession($session)
        ->get(CrossEnvironmentComparePage::getUrl(parameters: [
            'source_environment_id' => (int) $fixture['sourceEnvironment']->getKey(),
            'target_environment_id' => (int) $hiddenTarget->getKey(),
            'policy_type' => ['deviceConfiguration'],
        ], panel: 'admin'))
        ->assertNotFound();

    expect(OperationRun::query()->count())->toBe(0);
});
