<?php

namespace App\Services\Photobooth;

use App\Jobs\ProcessPhotoSecurityScan;
use App\Models\Emotion;
use App\Models\Event;
use App\Models\EventPackage;
use App\Models\Photo;
use App\Services\Packages\PackageLimitEvaluator;
use App\Services\Packages\PackageUsageTracker;
use App\Services\Storage\EventStorageManager;
use App\Support\ImageHelper;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class PhotoboothIngestService
{
    private bool $hasFilenameColumn;

    private bool $hasPathColumn;

    public function __construct(
        private readonly EventStorageManager $storageManager,
        private readonly PackageLimitEvaluator $packageLimitEvaluator,
        private readonly PackageUsageTracker $packageUsageTracker,
    ) {
        $this->hasFilenameColumn = Photo::supportsFilenameColumn();
        $this->hasPathColumn = Schema::hasColumn('photos', 'path');
    }

    public function ingest(Event $event, ?int $maxFiles = null, string $ingestSource = Photo::SOURCE_PHOTOBOOTH): array
    {
        $event->loadMissing('photoboothSetting');
        $eventSetting = $event->photoboothSetting;
        $tenant = $event->tenant;

        if (! $tenant || ! $eventSetting) {
            return ['processed' => 0, 'skipped' => 0];
        }

        $importDisk = config('photobooth.import.disk', 'photobooth');
        $basePath = ltrim((string) $eventSetting->path, '/');
        if (str_starts_with($basePath, 'photobooth/')) {
            $basePath = substr($basePath, strlen('photobooth/'));
        }

        $disk = Storage::disk($importDisk);

        if ($basePath === '' || ! $disk->directoryExists($basePath)) {
            return ['processed' => 0, 'skipped' => 0];
        }

        $allowedExtensions = config('photobooth.import.allowed_extensions', ['jpg', 'jpeg', 'png', 'webp']);
        $limit = $maxFiles ?? (int) config('photobooth.import.max_files_per_run', 50);

        $files = collect($disk->files($basePath))
            ->filter(fn ($file) => $this->isAllowedFile($file, $allowedExtensions))
            ->sort()
            ->take($limit);

        if ($files->isEmpty()) {
            return ['processed' => 0, 'skipped' => 0];
        }

        $event->loadMissing(['tenant', 'eventPackage.package', 'eventPackages.package', 'storageAssignments.storageTarget']);
        $tenant->refresh();

        $violation = $this->packageLimitEvaluator->assessPhotoUpload($tenant, $event->id, $event);
        if ($violation !== null) {
            Log::warning('[Photobooth] Upload blocked due to package violation', [
                'event_id' => $event->id,
                'code' => $violation['code'],
            ]);

            return ['processed' => 0, 'skipped' => $files->count()];
        }

        $eventPackage = $this->packageLimitEvaluator->resolveEventPackageForPhotoUpload($tenant, $event->id, $event);

        $disk = $this->storageManager->getHotDiskForEvent($event);
        $processed = 0;
        $skipped = 0;

        foreach ($files as $file) {
            if ($this->reachedPhotoLimit($eventPackage)) {
                break;
            }

            try {
                $result = $this->importFile($event, $eventPackage, $disk, $importDisk, $file, $ingestSource);
                if ($result) {
                    $processed++;
                    Storage::disk($importDisk)->delete($file);
                } else {
                    $skipped++;
                }
            } catch (\Throwable $exception) {
                $skipped++;
                Log::error('[Photobooth] Failed to ingest file', [
                    'event_id' => $event->id,
                    'file' => $file,
                    'message' => $exception->getMessage(),
                ]);
            }
        }

        return ['processed' => $processed, 'skipped' => $skipped];
    }

    protected function importFile(
        Event $event,
        ?EventPackage $eventPackage,
        string $destinationDisk,
        string $importDisk,
        string $file,
        string $ingestSource,
    ): bool {
        $stream = Storage::disk($importDisk)->readStream($file);

        if (! $stream) {
            return false;
        }

        $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION) ?: 'jpg');
        $filename = Str::uuid().'.'.$extension;
        $eventSlug = $event->slug ?? 'event-'.$event->id;
        $destinationPath = "events/{$eventSlug}/photos/{$filename}";

        try {
            Storage::disk($destinationDisk)->put($destinationPath, $stream);
        } finally {
            if (is_resource($stream)) {
                fclose($stream);
            }
        }

        $thumbnailPath = "events/{$eventSlug}/thumbnails/{$filename}";
        $thumbnailRelative = ImageHelper::makeThumbnailOnDisk($destinationDisk, $destinationPath, $thumbnailPath, 640, 82);
        $thumbnailToStore = $thumbnailRelative ?? $destinationPath;

        // Create watermarked copies (non-destructive).
        $watermarkConfig = \App\Support\WatermarkConfigResolver::resolve($event);
        $watermarkedPath = $destinationPath;
        $watermarkedThumb = $thumbnailToStore;
        if ($watermarkConfig['type'] !== 'none' && ! empty($watermarkConfig['asset']) && ! ($watermarkConfig['serve_originals'] ?? false)) {
            $watermarkedPath = ImageHelper::copyWithWatermark(
                $destinationDisk,
                $destinationPath,
                "events/{$eventSlug}/watermarked/{$filename}",
                $watermarkConfig
            ) ?? $destinationPath;
            if ($thumbnailRelative) {
                $watermarkedThumb = ImageHelper::copyWithWatermark(
                    $destinationDisk,
                    $thumbnailToStore,
                    "events/{$eventSlug}/watermarked/thumbnails/{$filename}",
                    $watermarkConfig
                ) ?? $thumbnailToStore;
            } else {
                $watermarkedThumb = $watermarkedPath;
            }
        }

        $size = Storage::disk($destinationDisk)->size($destinationPath);
        $mimeType = Storage::disk($destinationDisk)->mimeType($destinationPath) ?? 'image/jpeg';
        $originalName = basename($file);

        $photo = null;

        DB::transaction(function () use (
            &$photo,
            $event,
            $eventPackage,
            $destinationDisk,
            $destinationPath,
            $thumbnailRelative,
            $thumbnailToStore,
            $watermarkedPath,
            $watermarkedThumb,
            $ingestSource,
            $mimeType,
            $size,
            $filename,
            $originalName,
        ) {
            $payload = [
                'event_id' => $event->id,
                'emotion_id' => $this->resolveEmotionId($event),
                'original_name' => $originalName,
                'mime_type' => $mimeType,
                'size' => $size,
                'file_path' => $watermarkedPath,
                'thumbnail_path' => $watermarkedThumb,
                'status' => 'pending',
                'guest_name' => $ingestSource,
                'ingest_source' => $ingestSource,
                'ip_address' => null,
            ];

            if ($this->hasFilenameColumn) {
                $payload['filename'] = $filename;
            }
            if ($this->hasPathColumn) {
                $payload['path'] = $destinationPath;
            }

            $photo = Photo::create($payload);

            $asset = $this->storageManager->recordAsset($event, $destinationDisk, $destinationPath, [
                'variant' => 'original',
                'mime_type' => $mimeType,
                'size_bytes' => $size,
                'checksum' => null,
                'status' => 'hot',
                'processed_at' => now(),
                'photo_id' => $photo->id,
            ]);

            $watermarkedAsset = null;
            if ($watermarkedPath !== $destinationPath) {
                $watermarkedAsset = $this->storageManager->recordAsset($event, $destinationDisk, $watermarkedPath, [
                    'variant' => 'watermarked',
                    'mime_type' => $mimeType,
                    'size_bytes' => Storage::disk($destinationDisk)->exists($watermarkedPath)
                        ? Storage::disk($destinationDisk)->size($watermarkedPath)
                        : null,
                    'status' => 'hot',
                    'processed_at' => now(),
                    'photo_id' => $photo->id,
                    'meta' => [
                        'source_variant_id' => $asset->id,
                    ],
                ]);
            }

            if ($thumbnailRelative) {
                $this->storageManager->recordAsset($event, $destinationDisk, $thumbnailRelative, [
                    'variant' => 'thumbnail',
                    'mime_type' => 'image/jpeg',
                    'status' => 'hot',
                    'processed_at' => now(),
                    'photo_id' => $photo->id,
                ]);
            }
            if ($watermarkedThumb !== $thumbnailToStore) {
                $this->storageManager->recordAsset($event, $destinationDisk, $watermarkedThumb, [
                    'variant' => 'watermarked_thumbnail',
                    'mime_type' => 'image/jpeg',
                    'status' => 'hot',
                    'processed_at' => now(),
                    'photo_id' => $photo->id,
                    'size_bytes' => Storage::disk($destinationDisk)->exists($watermarkedThumb)
                        ? Storage::disk($destinationDisk)->size($watermarkedThumb)
                        : null,
                    'meta' => [
                        'source_variant_id' => $watermarkedAsset?->id ?? $asset->id,
                    ],
                ]);
            }

            $photo->update(['media_asset_id' => $asset->id]);

            $dimensions = @getimagesize(Storage::disk($destinationDisk)->path($destinationPath));

            if ($dimensions !== false) {
                $photo->update([
                    'width' => Arr::get($dimensions, 0),
                    'height' => Arr::get($dimensions, 1),
                ]);
            }

            if ($eventPackage) {
                $previousUsed = $eventPackage->used_photos;
                $eventPackage->increment('used_photos');
                $eventPackage->refresh();
                $this->packageUsageTracker->recordPhotoUsage($eventPackage, $previousUsed, 1);
            }
        });

        ProcessPhotoSecurityScan::dispatch($photo->id);

        return true;
    }

    protected function isAllowedFile(string $file, array $extensions): bool
    {
        $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION) ?: '');

        return $extension !== '' && in_array($extension, $extensions, true);
    }

    protected function reachedPhotoLimit(?EventPackage $eventPackage): bool
    {
        if (! $eventPackage || ! $eventPackage->package) {
            return false;
        }

        $limit = $eventPackage->effectivePhotoLimit();

        if ($limit === null) {
            return false;
        }

        return $limit > 0 && $eventPackage->used_photos >= $limit;
    }

    protected function resolveEmotionId(Event $event): ?int
    {
        if (! Photo::hasColumn('emotion_id')) {
            return null;
        }

        $existing = $event->photos()->value('emotion_id');

        if ($existing) {
            return $existing;
        }

        return Emotion::query()->value('id');
    }
}
