<?php

namespace App\Services\Works;

use App\Models\File;
use App\Models\Work;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class WorkOperationsService
{
    public function __construct(
        private WorkAssignmentService $workAssignmentService,
    ) {}

    /**
     * @param  iterable<int, UploadedFile>  $uploads
     */
    public function storeAttachmentsFromUploads(Work $work, iterable $uploads, ?int $uploadedByUserId): void
    {
        $disk = Work::WORK_FILES_DISK;
        $dir = 'work-files/'.$work->id;

        DB::transaction(function () use ($work, $uploads, $uploadedByUserId, $disk, $dir): void {
            foreach ($uploads as $upload) {
                $original = Str::limit($upload->getClientOriginalName(), 240, '…');
                $ext = strtolower((string) ($upload->getClientOriginalExtension() ?: $upload->guessExtension() ?: 'bin'));
                $filename = (string) Str::uuid().'.'.$ext;
                $relativePath = $dir.'/'.$filename;

                $localPath = $upload->getRealPath() ?: $upload->getPathname();
                $source = $localPath !== '' && $localPath !== false ? @fopen($localPath, 'rb') : false;
                if ($source === false) {
                    throw new \RuntimeException('No se pudo leer el archivo temporal.');
                }
                try {
                    $written = Storage::disk($disk)->writeStream($relativePath, $source);
                } finally {
                    fclose($source);
                }

                if ($written === false || ! Storage::disk($disk)->exists($relativePath)) {
                    throw new \RuntimeException('No se pudo guardar el archivo en el almacenamiento.');
                }

                File::query()->create([
                    'disk' => $disk,
                    'path' => $relativePath,
                    'name' => $original,
                    'mime_type' => Str::limit((string) $upload->getMimeType(), 120, '') ?: 'application/octet-stream',
                    'size_bytes' => $upload->getSize(),
                    'work_id' => $work->id,
                    'uploaded_by_user_id' => $uploadedByUserId,
                ]);
            }
        });
    }

    public function deleteAttachment(File $file): void
    {
        DB::transaction(function () use ($file): void {
            $file->deleteFromStorage();
            $file->delete();
        });
    }

    /**
     * @param  array<string, mixed>  $attributes  Salida de WorkCoordinatorForm::persistenceAttributesForCoordinator()
     */
    public function applyCoordinatorSnapshot(Work $work, array $attributes): void
    {
        $work->update($attributes);
    }

    public function applyTechnicianClosure(Work $work, ?string $clearanceDatetime, string $internalStatus): void
    {
        $previousInternal = (string) $work->internal_status;

        $work->update([
            'clearance_datetime' => trim((string) $clearanceDatetime) !== '' ? $clearanceDatetime : null,
            'internal_status' => $internalStatus,
        ]);

        $this->workAssignmentService->appendTechnicianAssignmentLogIfApplicable(
            $work->fresh(),
            $previousInternal,
            $internalStatus,
        );
    }

    public function applyNocTracking(Work $work, string $nocTrackingStatus, ?string $nocComment, ?int $actingUserId): void
    {
        $work->update([
            'noc_id' => $actingUserId,
            'noc_tracking_status' => $nocTrackingStatus,
            'noc_comment' => trim((string) $nocComment) !== '' ? trim((string) $nocComment) : null,
        ]);
    }
}
