<?php

declare(strict_types=1);

namespace App\Services;

use App\Enums\AssetDocumentType;
use App\Models\Asset;
use App\Models\AssetDocument;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

/**
 * Storage and lifecycle for asset documents — the part a bare
 * `AssetDocument::create()` cannot do safely on its own: generating a safe
 * internal filename (never the one the browser sent) and keeping the stored
 * file and its database row in lock-step on both create and delete.
 */
class AssetDocumentService
{
    /**
     * @param  bool  $silent  Skip the per-document `created` audit entry —
     *                        set by the bulk import path, which writes one
     *                        summary audit event for the whole batch instead
     *                        of one per attached document. See
     *                        AssetImportModule::create() for the same
     *                        pattern on Asset itself.
     * @param  int|null  $uploadedBy  Explicit actor, for the import path
     *                                (which runs as the uploading user but
     *                                outside a request where Auth::id()
     *                                would still resolve correctly — kept
     *                                explicit rather than relied upon).
     */
    public function attach(
        Asset $asset,
        UploadedFile $file,
        AssetDocumentType $type,
        bool $silent = false,
        ?int $uploadedBy = null,
    ): AssetDocument {
        $path = $file->storeAs(
            'asset-documents/'.$asset->id,
            Str::uuid()->toString().'.'.self::extensionForMime((string) $file->getMimeType()),
            'local',
        );

        $document = new AssetDocument;

        if ($silent) {
            $document->disableAuditing();
        }

        $document->asset_id = $asset->id;
        $document->document_type = $type;
        $document->original_filename = $file->getClientOriginalName();
        $document->stored_path = $path;
        $document->mime_type = (string) $file->getMimeType();
        $document->file_size = $file->getSize() ?: 0;
        $document->uploaded_by = $uploadedBy ?? Auth::id();
        $document->save();

        return $document;
    }

    /**
     * The bulk-import counterpart to attach(): the file's bytes are already
     * sitting on the private disk (validated and content-sniffed at upload
     * time by AssetDocumentImportService::ingest()), so this only needs to
     * copy them into the asset's own folder and write the row — there is no
     * UploadedFile to re-validate.
     */
    public function attachFromPath(
        Asset $asset,
        string $sourcePath,
        string $originalFilename,
        string $mimeType,
        int $fileSize,
        AssetDocumentType $type,
        bool $silent = false,
        ?int $uploadedBy = null,
    ): AssetDocument {
        $destination = 'asset-documents/'.$asset->id.'/'.Str::uuid()->toString().'.'.self::extensionForMime($mimeType);

        Storage::disk('local')->copy($sourcePath, $destination);

        $document = new AssetDocument;

        if ($silent) {
            $document->disableAuditing();
        }

        $document->asset_id = $asset->id;
        $document->document_type = $type;
        $document->original_filename = $originalFilename;
        $document->stored_path = $destination;
        $document->mime_type = $mimeType;
        $document->file_size = $fileSize;
        $document->uploaded_by = $uploadedBy;
        $document->save();

        return $document;
    }

    public function delete(AssetDocument $document): void
    {
        Storage::disk('local')->delete($document->stored_path);
        $document->delete();
    }

    /**
     * Maps a sniffed content type to a safe internal file extension — never
     * derived from a client-supplied filename or extension, which is
     * untrusted input. The single source both attach() and
     * attachFromPath() use, so a bulk-imported document and a manually
     * uploaded one can never land on different extensions for the same
     * content type.
     */
    public static function extensionForMime(string $mimeType): string
    {
        return match ($mimeType) {
            'image/png' => 'png',
            'image/jpeg' => 'jpg',
            default => 'pdf',
        };
    }
}
