<?php

namespace App\Commands;

use App\Commands\Concerns\InteractsWithPijul;
use LaravelZero\Framework\Commands\Command;

class PijulCreditWithMessagesCommand extends Command
{
    use InteractsWithPijul;

    /**
     * @var string
     */
    protected $signature = 'pijul:credit-with-messages
                            {file : The file to annotate}
                            {--repository= : Work with the repository at PATH}
                            {--channel= : Work with CHANNEL instead of the current channel}
                            {--no-prompt : Abort rather than prompt for input}';

    /**
     * @var string
     */
    protected $description = 'Show pijul credit output with change messages inline';

    /**
     * @var array<int, string>
     */
    protected $aliases = ['pijul-credit-with-messages'];

    /**
     * @var array<string, string>
     */
    private array $changeMessages = [];

    public function handle(): int
    {
        $output = $this->runPijul($this->creditArguments())->output();

        foreach (preg_split("/\r\n|\n|\r/", rtrim($output, "\r\n")) ?: [] as $line) {
            $this->line($this->isHashLine($line) ? $this->formatHashLine($line) : $line);
        }

        if (str_ends_with($output, "\n\n")) {
            $this->newLine();
        }

        return self::SUCCESS;
    }

    /**
     * @return array<int, string>
     */
    private function creditArguments(): array
    {
        $arguments = ['credit'];

        if (($repository = $this->option('repository')) !== null) {
            $arguments[] = '--repository';
            $arguments[] = $repository;
        }

        if (($channel = $this->option('channel')) !== null) {
            $arguments[] = '--channel';
            $arguments[] = $channel;
        }

        if ($this->option('no-prompt')) {
            $arguments[] = '--no-prompt';
        }

        $arguments[] = (string) $this->argument('file');

        return $arguments;
    }

    private function isHashLine(string $line): bool
    {
        return preg_match('/^[A-Z0-9]+(?:\s*,\s*[A-Z0-9]+)*$/', $line) === 1;
    }

    private function formatHashLine(string $line): string
    {
        $hashes = preg_split('/\s*,\s*/', trim($line)) ?: [];
        $formatted = [];

        foreach ($hashes as $hash) {
            $formatted[] = sprintf('%s (%s)', $hash, $this->resolveMessage($hash));
        }

        return implode(', ', $formatted);
    }

    private function resolveMessage(string $hash): string
    {
        if (array_key_exists($hash, $this->changeMessages)) {
            return $this->changeMessages[$hash];
        }

        $changeArguments = ['change'];

        if (($repository = $this->option('repository')) !== null) {
            $changeArguments[] = '--repository';
            $changeArguments[] = $repository;
        }

        if ($this->option('no-prompt')) {
            $changeArguments[] = '--no-prompt';
        }

        $changeArguments[] = $hash;

        $rawOutput = $this->runPijul($changeArguments)->output();

        if (preg_match('/^message = "(.*)"$/m', $rawOutput, $matches) !== 1) {
            return $this->changeMessages[$hash] = '<message unavailable>';
        }

        return $this->changeMessages[$hash] = str_replace('\"', '"', $matches[1]);
    }
}