<?php

namespace App\Modules\Exports\Jobs;

use App\Modules\Admin\Tenancy\Services\TenantContext;
use App\Modules\Exports\Events\ExportCompleted;
use App\Modules\Exports\Events\ExportFailed;
use App\Modules\Exports\Repositories\ExportRepository;
use App\Modules\Exports\Services\ExportHandlerRegistry;
use App\Modules\Exports\Support\ExportStatus;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Throwable;

class ExportProcessorJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        public readonly int $tenantId,
        public readonly int $exportId,
    ) {}

    public function handle(
        TenantContext $tenantContext,
        ExportHandlerRegistry $registry,
        ExportRepository $exports,
    ): void {
        $tenantContext->runForTenant($this->tenantId, function () use ($registry, $exports): void {
            $current = $exports->findForTenant($this->tenantId, $this->exportId);
            if ($current === null) {
                return;
            }
            if ($current->status === ExportStatus::COMPLETED) {
                return;
            }
            if ($current->status === ExportStatus::FAILED) {
                return;
            }

            $export = $exports->claimForProcessing($this->tenantId, $this->exportId);
            if ($export === null) {
                return;
            }

            try {
                $registry->handler($export->type)->handle($export);
                $final = $exports->markCompletedFromProcessing($this->tenantId, $this->exportId);
                if ($final !== null) {
                    event(new ExportCompleted($final));
                }
            } catch (Throwable $e) {
                $this->markFailed($exports, $e);
            }
        });
    }

    private function markFailed(ExportRepository $exports, Throwable $e): void
    {
        $row = $exports->findForTenant($this->tenantId, $this->exportId);
        if ($row === null) {
            return;
        }
        if ($row->file_path) {
            Storage::disk('local')->delete($row->file_path);
        }
        $msg = Str::limit($e->getMessage(), 2000, '');
        $failed = $exports->markFailedFromProcessing($this->tenantId, $this->exportId, [
            'status' => ExportStatus::FAILED,
            'failed_at' => now(),
            'error_message' => $msg,
            'file_path' => null,
            'file_name' => null,
            'mime_type' => null,
        ]);
        if ($failed !== null) {
            event(new ExportFailed($failed, $msg));
        }
    }
}
