<?php

use App\Prompts\LiveSuggestPrompt;
use Illuminate\Process\PendingProcess;
use Illuminate\Support\Facades\Process;
use Laravel\Prompts\Key;
use Laravel\Prompts\MultiSelectPrompt;

it('lists known intent tags', function () {
    Process::fake(function (PendingProcess $process) {
        return match ($process->command) {
            ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
                ['message' => 'feat(pijul-qol): first'],
                ['message' => 'feat(init): second'],
                ['message' => 'plain message'],
                ['message' => 'feat(pijul-qol): duplicate'],
            ], JSON_THROW_ON_ERROR)),
            default => Process::result('', 'Unexpected command.', 1),
        };
    });

    $this->artisan('pijul:record-with-intent-tags', ['--list-tags' => true])
        ->expectsOutput('feat(pijul-qol)')
        ->expectsOutput('feat(init)')
        ->assertExitCode(0);

    Process::assertDidntRun(fn (PendingProcess $process) => is_array($process->command)
        && ($process->command[1] ?? null) === 'record');
});

it('records a change with a reused intent tag', function () {
    Process::fake(function (PendingProcess $process) {
        return match (true) {
            $process->command === ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
                ['message' => 'feat(pijul-qol): first'],
                ['message' => 'feat(init): second'],
            ], JSON_THROW_ON_ERROR)),
            $process->command === ['pijul', 'record', 'app']
                && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture' => tap(
                    Process::result('', 'Captured Pijul hunks.', 1),
                    function () use ($process): void {
                        file_put_contents(
                            (string) $process->environment['ANI_PIJUL_CAPTURE_PATH'],
                            <<<'TEXT'
message = ""

# Hunks

1. Edit in "README.md"
+ README change

2. Edit in "app/Console.php"
+ Console change
TEXT
                        );
                    },
                ),
            $process->command === ['pijul', 'record', 'app']
                && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
                && ($process->environment['ANI_PIJUL_MESSAGE'] ?? null) === 'feat(pijul-qol): add laravel command'
                && ($process->environment['ANI_PIJUL_SELECTED_HUNKS'] ?? null) === '[1]' => Process::result(''),
            default => Process::result('', 'Unexpected command.', 1),
        };
    });

    LiveSuggestPrompt::fake(['f', 'e', 'a', 't', '(', 'p', 'i', 'j', 'u', 'l', '-', 'q', 'o', 'l', ')', Key::ENTER, Key::SPACE, Key::ENTER]);

    $this->artisan('pijul:record-with-intent-tags', ['prefixes' => ['app']])
        ->expectsOutputToContain('Known tags: feat(pijul-qol), feat(init)')
        ->expectsQuestion('message', 'add laravel command')
        ->assertExitCode(0);

    Process::assertRanTimes(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app'], 2);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app']
        && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture'
        && ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
        && ! $process->tty
        && $process->timeout === null);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', 'app']
        && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
        && ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
        && ! $process->tty
        && $process->timeout === null);
});

it('records selected hunks when an explicit message is provided interactively', function () {
    Process::fake(function (PendingProcess $process) {
        return match (true) {
            $process->command === ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
                ['message' => 'feat(pijul-qol): first'],
            ], JSON_THROW_ON_ERROR)),
            $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
                && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture' => tap(
                    Process::result('', 'Captured Pijul hunks.', 1),
                    function () use ($process): void {
                        file_put_contents(
                            (string) $process->environment['ANI_PIJUL_CAPTURE_PATH'],
                            <<<'TEXT'
message = ""

# Hunks

1. Edit in "README.md"
+ README change
TEXT
                        );
                    },
                ),
            $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
                && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
                && ($process->environment['ANI_PIJUL_MESSAGE'] ?? null) === 'ship it'
                && ($process->environment['ANI_PIJUL_SELECTED_HUNKS'] ?? null) === '[1]' => Process::result(''),
            default => Process::result('', 'Unexpected command.', 1),
        };
    });

    MultiSelectPrompt::fake([Key::ENTER]);

    $this->artisan('pijul:record-with-intent-tags', [
        '--message' => 'ship it',
        '--all' => true,
        '--description' => 'desc',
        'prefixes' => ['app'],
    ])
        ->expectsOutputToContain('Known tags: feat(pijul-qol)')
        ->assertExitCode(0);

    Process::assertRanTimes(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app'], 2);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
        && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'capture'
        && ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
        && ! $process->tty
        && $process->timeout === null);
    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '--all', '--description', 'desc', 'app']
        && ($process->environment['ANI_PIJUL_EDITOR_MODE'] ?? null) === 'apply'
        && ! str_contains((string) ($process->environment['EDITOR'] ?? ''), "'")
        && ! $process->tty
        && $process->timeout === null);
});

it('still forwards the message directly when the command runs non-interactively', function () {
    Process::fake(function (PendingProcess $process) {
        return match ($process->command) {
            ['pijul', 'log', '--output-format', 'json', '--limit', '500'] => Process::result(json_encode([
                ['message' => 'feat(pijul-qol): first'],
            ], JSON_THROW_ON_ERROR)),
            ['pijul', 'record', '-m', 'ship it', '--all', '--description', 'desc', 'app'] => Process::result(''),
            default => Process::result('', 'Unexpected command.', 1),
        };
    });

    $kernel = app(\Illuminate\Contracts\Console\Kernel::class);
    $artisan = new ReflectionMethod($kernel, 'getArtisan');
    $artisan->setAccessible(true);
    $command = $artisan->invoke($kernel)->find('pijul:record-with-intent-tags');
    $input = new Symfony\Component\Console\Input\ArrayInput([
        'command' => 'pijul:record-with-intent-tags',
        '--message' => 'ship it',
        '--all' => true,
        '--description' => 'desc',
        'prefixes' => ['app'],
    ]);
    $input->setInteractive(false);

    $exitCode = $command->run($input, new Symfony\Component\Console\Output\BufferedOutput);

    expect($exitCode)->toBe(0);

    Process::assertRan(fn (PendingProcess $process) => $process->command === ['pijul', 'record', '-m', 'ship it', '--all', '--description', 'desc', 'app']
        && $process->environment === []
        && ! $process->tty);
});