<?php

namespace Tests\Feature;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Event;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Tests\TestCase;

class SentryReportingTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        config(['sentry.dsn' => 'https://example@sentry.test/1']);
    }

    public function test_reports_server_errors_to_sentry(): void
    {
        $fake = new class
        {
            public int $captured = 0;

            public function captureException(mixed $exception = null): void
            {
                $this->captured++;
            }
        };

        $this->app->instance('sentry', $fake);

        $handler = $this->app->make(\App\Exceptions\Handler::class);

        $handler->report(new \RuntimeException('boom'));

        $this->assertSame(1, $fake->captured, 'Sentry should receive server errors');
    }

    public function test_ignores_client_errors_for_sentry(): void
    {
        $fake = new class
        {
            public int $captured = 0;

            public function captureException(mixed $exception = null): void
            {
                $this->captured++;
            }
        };

        $this->app->instance('sentry', $fake);

        $handler = $this->app->make(\App\Exceptions\Handler::class);

        $handler->report(new HttpException(404));

        $this->assertSame(0, $fake->captured, 'Sentry should ignore 4xx errors');
    }

    public function test_reports_scheduled_task_failures_to_sentry(): void
    {
        $fake = new class
        {
            public int $captured = 0;

            public function captureException(mixed $exception = null): void
            {
                $this->captured++;
            }
        };

        $this->app->instance('sentry', $fake);

        Artisan::call('schedule:list', [
            '--json' => true,
            '--no-interaction' => true,
        ]);

        $schedule = $this->app->make(Schedule::class);
        $task = collect($schedule->events())
            ->first(fn ($event) => str_contains((string) $event->command, 'storage:monitor'));

        $this->assertNotNull($task, 'Expected scheduled storage:monitor task');

        $task->finish($this->app, 1);

        $this->assertSame(1, $fake->captured, 'Sentry should receive scheduled task failures');
    }

    public function test_reports_queue_failures_to_sentry(): void
    {
        $fake = new class
        {
            public int $captured = 0;

            public function captureException(mixed $exception = null): void
            {
                $this->captured++;
            }
        };

        $this->app->instance('sentry', $fake);

        $job = new class implements \Illuminate\Contracts\Queue\Job
        {
            public function uuid(): ?string
            {
                return null;
            }

            public function getJobId(): string
            {
                return 'job-id';
            }

            public function payload(): array
            {
                return [];
            }

            public function fire(): void {}

            public function release($delay = 0): void {}

            public function isReleased(): bool
            {
                return false;
            }

            public function delete(): void {}

            public function isDeleted(): bool
            {
                return false;
            }

            public function isDeletedOrReleased(): bool
            {
                return false;
            }

            public function attempts(): int
            {
                return 1;
            }

            public function hasFailed(): bool
            {
                return false;
            }

            public function markAsFailed(): void {}

            public function fail($e = null): void {}

            public function maxTries(): ?int
            {
                return null;
            }

            public function maxExceptions(): ?int
            {
                return null;
            }

            public function timeout(): ?int
            {
                return null;
            }

            public function retryUntil(): ?int
            {
                return null;
            }

            public function getName(): string
            {
                return 'FakeJob';
            }

            public function resolveName(): string
            {
                return 'FakeJob';
            }

            public function resolveQueuedJobClass(): string
            {
                return 'FakeJob';
            }

            public function getConnectionName(): string
            {
                return 'redis';
            }

            public function getQueue(): string
            {
                return 'default';
            }

            public function getRawBody(): string
            {
                return '{}';
            }
        };

        Event::dispatch(new JobFailed('redis', $job, new \RuntimeException('Queue failure')));

        $this->assertSame(1, $fake->captured, 'Sentry should receive queue failures');
    }
}
