<?php

namespace App\Services\Media;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;

class MediaService
{
    public function syncCollection(
        HasMedia $model,
        string $collection,
        ?array $newFiles,
        ?array $removeIds,
        int $maxFiles,
        ?string $type = null,
        bool $required = false,
        ?string $errorKey = null,
        ?array $s3Keys = null,
    ): void {
        // Handle removals
        if ($maxFiles === 1 && (! empty($newFiles) || ! empty($s3Keys))) {
            $model->clearMediaCollection($collection);
        } elseif ($removeIds !== null && $removeIds !== []) {
            $model->getMedia($collection)
                ->whereIn('id', $removeIds)
                ->each->delete();
        }

        // Determine items to add (S3 keys take priority)
        $useS3 = ! empty($s3Keys);
        $items = $useS3
            ? array_values(array_filter($s3Keys))
            : array_values(array_filter($newFiles ?? []));

        if ($maxFiles === 1) {
            $items = array_slice($items, 0, 1);
        }

        $currentCount = $model->getMedia($collection)->count();

        if ($currentCount + count($items) > $maxFiles) {
            throw ValidationException::withMessages([
                $errorKey ?? $collection => "Maksimal {$maxFiles} gambar per item.",
            ]);
        }

        foreach ($items as $item) {
            if ($useS3) {
                $this->registerS3Key($model, $item, $collection, $type);
            } else {
                $this->addUploadedFile($model, $item, $collection, $type);
            }
        }

        if ($model instanceof Model) {
            $model->unsetRelation('media');
        }

        if ($required && $model->getMedia($collection)->isEmpty()) {
            throw ValidationException::withMessages([
                $errorKey ?? $collection => 'Foto wajib diisi.',
            ]);
        }
    }

    public function addUploadedFile(
        HasMedia $model,
        UploadedFile $file,
        string $collection,
        ?string $type = null,
    ): Media {
        return $model->addMedia($file)
            ->withCustomProperties($this->customProperties($model, $type ?? $collection))
            ->toMediaCollection($collection);
    }

    /**
     * @param  HasMedia&InteractsWithMedia  $model
     */
    public function registerS3Key(
        HasMedia $model,
        string $s3Key,
        string $collection,
        ?string $type = null,
    ): Media {
        $disk = config('filesystems.default') === 's3' ? 's3' : config('filesystems.default', 'public');
        try {
            return $model->addMediaFromDisk($s3Key, $disk)
                ->withCustomProperties($this->customProperties($model, $type ?? $collection))
                ->toMediaCollection($collection);
        } finally {
            Storage::disk($disk)->delete($s3Key);
        }
    }

    public function addBase64Image(
        HasMedia $model,
        string $base64Photo,
        string $collection,
        ?string $type = null,
        string $extension = 'jpg',
    ): Media {
        $image = base64_decode(
            (string) preg_replace('#^data:image/\w+;base64,#i', '', $base64Photo),
            true,
        );

        if ($image === false) {
            throw ValidationException::withMessages([
                'photo' => 'Foto tidak valid.',
            ]);
        }

        $filename = sprintf('%s.%s', Str::uuid(), $extension);
        $tempPath = 'temp/'.$filename;

        Storage::disk('local')->put($tempPath, $image);

        try {
            return $model->addMedia(Storage::disk('local')->path($tempPath))
                ->usingFileName($filename)
                ->withCustomProperties($this->customProperties($model, $type ?? $collection))
                ->toMediaCollection($collection);
        } finally {
            Storage::disk('local')->delete($tempPath);
        }
    }

    public function replaceSingleFile(
        HasMedia $model,
        UploadedFile $file,
        string $collection,
        ?string $type = null,
    ): Media {
        $model->clearMediaCollection($collection);

        return $this->addUploadedFile($model, $file, $collection, $type);
    }

    public function replaceSingleS3Key(
        HasMedia $model,
        string $s3Key,
        string $collection,
        ?string $type = null,
    ): Media {
        $model->clearMediaCollection($collection);

        return $this->registerS3Key($model, $s3Key, $collection, $type);
    }

    private function customProperties(HasMedia $model, ?string $type): array
    {
        $properties = [
            'module' => method_exists($model, 'mediaModuleName')
                ? $model::mediaModuleName()
                : 'media',
        ];

        if ($type !== null && $type !== '') {
            $properties['type'] = $type;
        }

        return $properties;
    }
}
