<?php

declare(strict_types=1);

use App\Models\OperationRun;
use App\Support\OpsUx\OperationRunProgressContract;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('returns counted progress for running runs with trustworthy processed totals', function (): void {
    $run = OperationRun::factory()->create([
        'status' => 'running',
        'outcome' => 'pending',
        'summary_counts' => [
            'total' => 10,
            'processed' => 4,
        ],
        'started_at' => now()->subMinute(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('counted')
        ->and($progress['display'])->toBe('counted')
        ->and($progress['processed'])->toBe(4)
        ->and($progress['total'])->toBe(10)
        ->and($progress['percent'])->toBe(40)
        ->and($progress['label'])->toBe('4 / 10 processed (40%)');
});

it('clamps counted progress into a truthful visible range', function (): void {
    $run = OperationRun::factory()->create([
        'status' => 'running',
        'outcome' => 'pending',
        'summary_counts' => [
            'total' => 10,
            'processed' => 15,
        ],
        'started_at' => now()->subMinute(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('counted')
        ->and($progress['processed'])->toBe(10)
        ->and($progress['total'])->toBe(10)
        ->and($progress['percent'])->toBe(100)
        ->and($progress['label'])->toBe('10 / 10 processed (100%)');
});

it('keeps queued runs activity only even when planned totals exist', function (): void {
    $run = OperationRun::factory()->create([
        'status' => 'queued',
        'outcome' => 'pending',
        'summary_counts' => [
            'total' => 10,
            'processed' => 0,
        ],
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('activity')
        ->and($progress['display'])->toBe('indeterminate')
        ->and($progress['label'])->toBe('Waiting for worker.')
        ->and($progress['percent'])->toBeNull();
});

it('returns no progress for terminal runs even when retained counts exist', function (): void {
    $run = OperationRun::factory()->create([
        'status' => 'completed',
        'outcome' => 'succeeded',
        'summary_counts' => [
            'total' => 10,
            'processed' => 10,
        ],
        'started_at' => now()->subMinutes(2),
        'completed_at' => now()->subSecond(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('none')
        ->and($progress['display'])->toBe('none')
        ->and($progress['label'])->toBeNull()
        ->and($progress['percent'])->toBeNull();
});

it('does not let outcome counters masquerade as counted progress', function (): void {
    $run = OperationRun::factory()->create([
        'status' => 'running',
        'outcome' => 'pending',
        'summary_counts' => [
            'succeeded' => 4,
            'failed' => 1,
            'skipped' => 2,
        ],
        'started_at' => now()->subMinute(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('activity')
        ->and($progress['display'])->toBe('indeterminate')
        ->and($progress['label'])->toBe('Progress details pending.')
        ->and($progress['percent'])->toBeNull();
});

it('classifies repo-real baseline evidence capture runs as phased fallback', function (): void {
    $run = OperationRun::factory()->create([
        'type' => 'baseline_capture',
        'status' => 'running',
        'outcome' => 'pending',
        'context' => [
            'baseline_capture' => [
                'evidence_capture' => [
                    'requested' => 10,
                    'succeeded' => 3,
                    'skipped' => 1,
                ],
                'resume_token' => 'resume-123',
            ],
        ],
        'started_at' => now()->subMinute(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('phased')
        ->and($progress['display'])->toBe('indeterminate')
        ->and($progress['label'])->toBe('Capturing evidence.')
        ->and($progress['percent'])->toBeNull();
});

it('uses canonical phase metadata when present for phased runs', function (): void {
    $run = OperationRun::factory()->create([
        'type' => 'baseline_compare',
        'status' => 'running',
        'outcome' => 'pending',
        'context' => [
            'progress' => [
                'phase' => [
                    'key' => 'persisting',
                    'label' => 'Saving comparison results.',
                ],
            ],
        ],
        'started_at' => now()->subMinute(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('phased')
        ->and($progress['display'])->toBe('indeterminate')
        ->and($progress['label'])->toBe('Saving comparison results.')
        ->and($progress['percent'])->toBeNull();
});

it('classifies aggregate multi-run work as composite fallback', function (): void {
    $run = OperationRun::factory()->create([
        'status' => 'running',
        'outcome' => 'pending',
        'summary_counts' => [
            'operation_count' => 3,
        ],
        'started_at' => now()->subMinute(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('composite')
        ->and($progress['display'])->toBe('indeterminate')
        ->and($progress['label'])->toBe('Composite progress pending.')
        ->and($progress['percent'])->toBeNull();
});

it('derives a environment review composite label from aggregate operation truth', function (): void {
    $run = OperationRun::factory()->create([
        'type' => 'environment.review.compose',
        'status' => 'running',
        'outcome' => 'pending',
        'summary_counts' => [
            'operation_count' => 3,
        ],
        'started_at' => now()->subMinute(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('composite')
        ->and($progress['display'])->toBe('indeterminate')
        ->and($progress['label'])->toBe('Review composition is aggregating 3 operations.')
        ->and($progress['percent'])->toBeNull();
});

it('uses explicit composite attention hints when present', function (): void {
    $run = OperationRun::factory()->create([
        'type' => 'environment.review.compose',
        'status' => 'running',
        'outcome' => 'pending',
        'summary_counts' => [
            'operation_count' => 4,
        ],
        'context' => [
            'progress' => [
                'composite' => [
                    'label' => 'Review composition is aggregating 4 operations. 1 failed operation currently needs review.',
                ],
            ],
        ],
        'started_at' => now()->subMinute(),
    ]);

    $progress = OperationRunProgressContract::forRun($run);

    expect($progress['capability'])->toBe('composite')
        ->and($progress['display'])->toBe('indeterminate')
        ->and($progress['label'])->toBe('Review composition is aggregating 4 operations. 1 failed operation currently needs review.')
        ->and($progress['percent'])->toBeNull();
});