<?php

namespace Sentry\Laravel\Features;

use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Console\Events as ConsoleEvents;
use Sentry\Breadcrumb;
use Sentry\Laravel\Integration;
use Sentry\State\Scope;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;

class ConsoleIntegration extends Feature
{
    private const FEATURE_KEY = 'command_info';

    public function isApplicable(): bool
    {
        return true;
    }

    public function onBoot(Dispatcher $events): void
    {
        $events->listen(ConsoleEvents\CommandStarting::class, [$this, 'commandStarting']);
        $events->listen(ConsoleEvents\CommandFinished::class, [$this, 'commandFinished']);
    }

    public function commandStarting(ConsoleEvents\CommandStarting $event): void
    {
        if (!$event->command) {
            return;
        }

        Integration::configureScope(static function (Scope $scope) use ($event): void {
            $scope->setTag('command', $event->command);
        });

        if ($this->isBreadcrumbFeatureEnabled(self::FEATURE_KEY)) {
            Integration::addBreadcrumb(new Breadcrumb(
                Breadcrumb::LEVEL_INFO,
                Breadcrumb::TYPE_DEFAULT,
                'artisan.command',
                'Starting Artisan command: ' . $event->command,
                [
                    'input' => $this->extractConsoleCommandInput($event->input),
                ]
            ));
        }
    }

    public function commandFinished(ConsoleEvents\CommandFinished $event): void
    {
        if ($this->isBreadcrumbFeatureEnabled(self::FEATURE_KEY)) {
            Integration::addBreadcrumb(new Breadcrumb(
                Breadcrumb::LEVEL_INFO,
                Breadcrumb::TYPE_DEFAULT,
                'artisan.command',
                'Finished Artisan command: ' . $event->command,
                [
                    'exit' => $event->exitCode,
                    'input' => $this->extractConsoleCommandInput($event->input),
                ]
            ));
        }

        // Flush any and all events that were possibly generated by the command
        Integration::flushEvents();

        Integration::configureScope(static function (Scope $scope): void {
            $scope->removeTag('command');
        });
    }

    /**
     * Extract the command input arguments if possible.
     *
     * @param \Symfony\Component\Console\Input\InputInterface|null $input
     *
     * @return string|null
     */
    private function extractConsoleCommandInput(?InputInterface $input): ?string
    {
        if ($input instanceof ArgvInput) {
            return (string)$input;
        }

        return null;
    }
}
