<?php

namespace Ignite\Core\Jobs;

use Ignite\Core\Entities\ReportDownload;
use Ignite\Core\Services\Reports\ReportDownloadService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Throwable;

/**
 * Thin Horizon adapter for one durable report download request.
 *
 * The job contains only the download identifier. All source reconstruction and file
 * generation remains in ReportDownloadService so retries never serialize builders,
 * HTTP requests, closures, or other process-bound state.
 */
class ProcessReportDownload implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    /** @var int */
    protected $reportDownloadId;

    public function __construct(int $reportDownloadId)
    {
        $this->reportDownloadId = $reportDownloadId;
        $this->onQueue(config('core.report_download.queue', 'report-download'));
    }

    /**
     * Process the download and leave transient failures eligible for Laravel retry.
     *
     * @throws Throwable Re-thrown so Horizon applies configured retry/backoff.
     */
    public function handle(ReportDownloadService $service): void
    {
        if ($this->attempts() > 1) {
            // A hard timeout terminates PHP before the catch block can return the
            // durable record to QUEUED. The next Horizon attempt repairs that state
            // before claiming it again. The consuming app must keep retry_after
            // longer than this job's timeout to prevent concurrent delivery.
            $download = ReportDownload::find($this->reportDownloadId);
            if ($download) {
                $download->releaseForQueueRetry();
            }
        }

        try {
            $service->process($this->reportDownloadId);
        } catch (Throwable $exception) {
            $download = ReportDownload::find($this->reportDownloadId);
            if ($download) {
                $download->releaseForQueueRetry();
            }

            throw $exception;
        }
    }

    /**
     * Mark the durable record failed only after Horizon exhausts all attempts.
     */
    public function failed(Throwable $exception): void
    {
        $download = ReportDownload::find($this->reportDownloadId);
        if ($download) {
            $download->markFailed($exception->getMessage());
        }
    }
}
