<?php

namespace App\Http\Controllers\Api\Tenant;

use App\Http\Controllers\Controller;
use App\Http\Requests\Tenant\PhotoStoreRequest;
use App\Http\Resources\Tenant\PhotoResource;
use App\Jobs\ProcessPhotoSecurityScan;
use App\Models\Event;
use App\Models\EventMediaAsset;
use App\Models\Photo;
use App\Services\Packages\PackageLimitEvaluator;
use App\Services\Packages\PackageUsageTracker;
use App\Services\Storage\EventStorageManager;
use App\Support\ApiError;
use App\Support\ImageHelper;
use App\Support\TenantMemberPermissions;
use App\Support\UploadStream;
use App\Support\WatermarkConfigResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

class PhotoController extends Controller
{
    public function __construct(
        private readonly EventStorageManager $eventStorageManager,
        private readonly PackageLimitEvaluator $packageLimitEvaluator,
        private readonly PackageUsageTracker $packageUsageTracker,
    ) {}

    /**
     * Display a listing of the event's photos.
     */
    public function index(Request $request, string $eventSlug): AnonymousResourceCollection
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::with([
            'tenant',
            'eventPackage.package',
            'eventPackages.package',
            'storageAssignments.storageTarget',
        ])->where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

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

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

        $limitSummary = $eventPackage
            ? $this->packageLimitEvaluator->summarizeEventPackage($eventPackage)
            : null;

        $query = Photo::where('event_id', $event->id)
            ->with('event')->withCount('likes');

        // Filters
        if ($request->has('status')) {
            $query->where('status', $request->status);
        }

        if ($request->boolean('featured')) {
            $query->where('is_featured', true);
        }

        if ($request->has('user_id')) {
            $query->where('uploader_id', $request->user_id);
        }

        if ($request->filled('ingest_source')) {
            $query->where('ingest_source', $request->string('ingest_source'));
        }

        $visibility = strtolower((string) $request->input('visibility', ''));
        if ($visibility === 'visible') {
            $query->where('status', '!=', 'hidden');
        } elseif ($visibility === 'hidden') {
            $query->where('status', 'hidden');
        }

        if ($request->filled('search')) {
            $term = strtolower(trim((string) $request->get('search')));
            $query->where(function ($inner) use ($term) {
                $inner->whereRaw('LOWER(original_name) LIKE ?', ['%'.$term.'%'])
                    ->orWhereRaw('LOWER(filename) LIKE ?', ['%'.$term.'%']);
            });
        }

        $direction = strtolower($request->get('sort', 'desc')) === 'asc' ? 'asc' : 'desc';
        $query->orderBy('created_at', $direction);

        $perPage = max(1, min((int) $request->get('per_page', 40), 100));
        $photos = $query->paginate($perPage);

        return PhotoResource::collection($photos)->additional([
            'limits' => $limitSummary,
        ]);
    }

    public function visibility(Request $request, string $eventSlug, Photo $photo): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

        if ($photo->event_id !== $event->id) {
            return ApiError::response(
                'photo_not_found',
                'Foto nicht gefunden',
                'Das Foto gehört nicht zu diesem Event.',
                Response::HTTP_NOT_FOUND,
                ['photo_id' => $photo->id]
            );
        }

        $validated = $request->validate([
            'visible' => ['required', 'boolean'],
        ]);

        $photo->status = $validated['visible'] ? 'approved' : 'hidden';
        $photo->save();
        $photo->load('event')->loadCount('likes');

        return response()->json([
            'message' => 'Photo visibility updated',
            'data' => new PhotoResource($photo),
        ]);
    }

    public function asset(Request $request, Event $event, Photo $photo, string $variant): StreamedResponse|JsonResponse
    {
        if ($photo->event_id !== $event->id) {
            return ApiError::response(
                'photo_not_found',
                'Foto nicht gefunden',
                'Das Foto gehört nicht zu diesem Event.',
                Response::HTTP_NOT_FOUND,
                ['photo_id' => $photo->id]
            );
        }

        $preferOriginals = (bool) ($event->settings['watermark_serve_originals'] ?? false);
        [$disk, $path, $mime] = $this->resolvePhotoVariant($photo, $variant, $preferOriginals);

        if (! $path) {
            return ApiError::response(
                'photo_unavailable',
                'Photo Unavailable',
                'The requested photo could not be loaded.',
                Response::HTTP_NOT_FOUND,
                [
                    'photo_id' => $photo->id,
                    'event_id' => $event->id,
                ]
            );
        }

        if (Str::startsWith($path, ['http://', 'https://'])) {
            return redirect()->away($path);
        }

        try {
            $storage = Storage::disk($disk);
        } catch (\Throwable $e) {
            Log::warning('Tenant photo asset disk unavailable', [
                'event_id' => $event->id,
                'photo_id' => $photo->id,
                'disk' => $disk,
                'error' => $e->getMessage(),
            ]);

            return ApiError::response(
                'photo_unavailable',
                'Photo Unavailable',
                'The requested photo could not be loaded.',
                Response::HTTP_NOT_FOUND,
                [
                    'photo_id' => $photo->id,
                    'event_id' => $event->id,
                ]
            );
        }

        foreach ($this->storagePathCandidates($path) as $candidate) {
            if (! $storage->exists($candidate)) {
                continue;
            }

            $stream = $storage->readStream($candidate);
            if (! $stream) {
                continue;
            }

            $size = null;
            try {
                $size = $storage->size($candidate);
            } catch (\Throwable $e) {
                $size = null;
            }

            if (! $mime) {
                try {
                    $mime = $storage->mimeType($candidate);
                } catch (\Throwable $e) {
                    $mime = null;
                }
            }

            $extension = pathinfo($candidate, PATHINFO_EXTENSION) ?: ($photo->mime_type ? explode('/', $photo->mime_type)[1] ?? 'jpg' : 'jpg');
            $suffix = $variant === 'thumbnail' ? '-thumb' : '';
            $filename = sprintf('fotospiel-event-%s-photo-%s%s.%s', $event->id, $photo->id, $suffix, $extension ?: 'jpg');

            $headers = [
                'Content-Type' => $mime ?? 'image/jpeg',
                'Cache-Control' => 'private, max-age=1800',
                'Content-Disposition' => 'inline; filename="'.$filename.'"',
            ];

            if ($size) {
                $headers['Content-Length'] = $size;
            }

            return response()->stream(function () use ($stream) {
                fpassthru($stream);
                fclose($stream);
            }, 200, $headers);
        }

        return ApiError::response(
            'photo_unavailable',
            'Photo Unavailable',
            'The requested photo could not be loaded.',
            Response::HTTP_NOT_FOUND,
            [
                'photo_id' => $photo->id,
                'event_id' => $event->id,
            ]
        );
    }

    /**
     * Store a newly uploaded photo.
     */
    public function store(PhotoStoreRequest $request, string $eventSlug): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

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

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

        $previousUsedPhotos = $eventPackage?->used_photos ?? 0;

        $validated = $request->validated();
        $file = $request->file('photo');

        if (! $file) {
            throw ValidationException::withMessages([
                'photo' => 'No photo file uploaded.',
            ]);
        }

        // Validate file type and size
        $allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
        if (! in_array($file->getMimeType(), $allowedTypes)) {
            throw ValidationException::withMessages([
                'photo' => 'Only JPEG, PNG, and WebP images are allowed.',
            ]);
        }

        if ($file->getSize() > 10 * 1024 * 1024) { // 10MB
            throw ValidationException::withMessages([
                'photo' => 'Photo size must be less than 10MB.',
            ]);
        }

        if ($tenant) {
            $violation = $this->packageLimitEvaluator->assessPhotoUpload(
                $tenant,
                $event->id,
                $event,
                $file->getSize()
            );

            if ($violation !== null) {
                return ApiError::response(
                    $violation['code'],
                    $violation['title'],
                    $violation['message'],
                    $violation['status'],
                    $violation['meta']
                );
            }
        }

        // Determine storage target
        $disk = $this->eventStorageManager->getHotDiskForEvent($event);

        // Generate unique filename
        $extension = $file->getClientOriginalExtension();
        $filename = Str::uuid().'.'.$extension;
        $path = "events/{$eventSlug}/photos/{$filename}";

        // Store original file
        UploadStream::putUploadedFile($disk, $path, $file);

        // Generate thumbnail
        $thumbnailPath = "events/{$eventSlug}/thumbnails/{$filename}";
        $thumbnailRelative = ImageHelper::makeThumbnailOnDisk($disk, $path, $thumbnailPath, 400);
        if ($thumbnailRelative) {
            $thumbnailPath = $thumbnailRelative;
        }

        // Apply watermark policy (in-place) to original and thumbnail where applicable.
        $watermarkConfig = WatermarkConfigResolver::resolve($event);
        if ($watermarkConfig['type'] !== 'none' && ! empty($watermarkConfig['asset'])) {
            ImageHelper::applyWatermarkOnDisk($disk, $path, $watermarkConfig);
            if ($thumbnailRelative) {
                ImageHelper::applyWatermarkOnDisk($disk, $thumbnailPath, $watermarkConfig);
            }
        }

        $watermarkConfig = WatermarkConfigResolver::resolve($event);
        $watermarkedPath = $path;
        $watermarkedThumb = $thumbnailPath;

        if ($watermarkConfig['type'] !== 'none' && ! empty($watermarkConfig['asset']) && ! ($watermarkConfig['serve_originals'] ?? false)) {
            $watermarkedPath = ImageHelper::copyWithWatermark($disk, $path, "events/{$eventSlug}/watermarked/{$filename}", $watermarkConfig) ?? $path;
            if ($thumbnailRelative) {
                $watermarkedThumb = ImageHelper::copyWithWatermark(
                    $disk,
                    $thumbnailPath,
                    "events/{$eventSlug}/watermarked/thumbnails/{$filename}",
                    $watermarkConfig
                ) ?? $thumbnailPath;
            }
        }

        $photoAttributes = [
            'event_id' => $event->id,
            'guest_name' => Photo::SOURCE_TENANT_ADMIN,
            'original_name' => $file->getClientOriginalName(),
            'mime_type' => $file->getMimeType(),
            'size' => $file->getSize(),
            'file_path' => $watermarkedPath,
            'thumbnail_path' => $watermarkedThumb,
            'width' => null, // Filled below
            'height' => null,
            'status' => 'approved',
            'uploader_id' => null,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'ingest_source' => Photo::SOURCE_TENANT_ADMIN,
        ];

        if (Photo::supportsFilenameColumn()) {
            $photoAttributes['filename'] = $filename;
        }
        if (Photo::hasColumn('path')) {
            $photoAttributes['path'] = $path;
        }

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

        // Record primary asset metadata
        $storedPath = Storage::disk($disk)->path($path);
        $checksum = file_exists($storedPath) ? hash_file('sha256', $storedPath) : hash_file('sha256', $file->getRealPath());
        $storedSize = Storage::disk($disk)->exists($path) ? Storage::disk($disk)->size($path) : $file->getSize();
        $asset = $this->eventStorageManager->recordAsset($event, $disk, $path, [
            'variant' => 'original',
            'mime_type' => $file->getMimeType(),
            'size_bytes' => $storedSize,
            'checksum' => $checksum,
            'status' => 'hot',
            'processed_at' => now(),
            'photo_id' => $photo->id,
        ]);

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

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

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

        // Get image dimensions
        [$width, $height] = getimagesize($file->getRealPath());
        $photo->update(['width' => $width, 'height' => $height]);

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

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

        $limitSummary = $eventPackage
            ? $this->packageLimitEvaluator->summarizeEventPackage($eventPackage)
            : null;

        $photo->load('event')->loadCount('likes');

        return response()->json([
            'message' => 'Photo uploaded successfully. Awaiting moderation.',
            'data' => new PhotoResource($photo),
            'moderation_notice' => 'Your photo has been uploaded and will be reviewed shortly.',
            'limits' => $limitSummary,
        ], 201);
    }

    /**
     * Display the specified photo.
     */
    public function show(Request $request, string $eventSlug, Photo $photo): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

        if ($photo->event_id !== $event->id) {
            return ApiError::response(
                'photo_not_found',
                'Foto nicht gefunden',
                'Das Foto gehört nicht zu diesem Event.',
                404,
                ['photo_id' => $photo->id]
            );
        }

        $photo->load('event')->loadCount('likes');
        $photo->increment('view_count');

        return response()->json([
            'data' => new PhotoResource($photo),
        ]);
    }

    /**
     * Update the specified photo (moderation or metadata).
     */
    public function update(Request $request, string $eventSlug, Photo $photo): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

        if ($photo->event_id !== $event->id) {
            return ApiError::response(
                'photo_not_found',
                'Foto nicht gefunden',
                'Das Foto gehört nicht zu diesem Event.',
                404,
                ['photo_id' => $photo->id]
            );
        }

        $validated = $request->validate([
            'status' => ['sometimes', 'in:pending,approved,rejected'],
            'moderation_notes' => ['sometimes', 'required_if:status,rejected', 'string', 'max:1000'],
            'caption' => ['sometimes', 'string', 'max:500'],
            'alt_text' => ['sometimes', 'string', 'max:255'],
        ]);

        if (isset($validated['status'])) {
            TenantMemberPermissions::ensureEventPermission($request, $event, 'photos:moderate');
        }

        $photo->update($validated);

        if ($validated['status'] ?? null === 'approved') {
            $photo->load('event')->loadCount('likes');
            // Trigger event for new photo notification
            // event(new \App\Events\PhotoApproved($photo)); // Implement later
        }

        return response()->json([
            'message' => 'Photo updated successfully',
            'data' => new PhotoResource($photo),
        ]);
    }

    /**
     * Remove the specified photo from storage.
     */
    public function destroy(Request $request, string $eventSlug, Photo $photo): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

        if ($photo->event_id !== $event->id) {
            return ApiError::response(
                'photo_not_found',
                'Foto nicht gefunden',
                'Das Foto gehört nicht zu diesem Event.',
                404,
                ['photo_id' => $photo->id]
            );
        }

        $assets = EventMediaAsset::where('photo_id', $photo->id)->get();

        foreach ($assets as $asset) {
            if (! is_string($asset->path) || $asset->path === '') {
                continue;
            }
            try {
                Storage::disk($asset->disk)->delete($asset->path);
            } catch (\Throwable $e) {
                Log::warning('Failed to delete asset from storage', [
                    'asset_id' => $asset->id,
                    'disk' => $asset->disk,
                    'path' => $asset->path,
                    'error' => $e->getMessage(),
                ]);
            }
        }

        // Ensure legacy paths are removed if assets missing
        if ($assets->isEmpty()) {
            $fallbackDisk = $this->eventStorageManager->getHotDiskForEvent($event);
            $paths = array_values(array_filter([
                is_string($photo->path) ? $photo->path : null,
                is_string($photo->thumbnail_path) ? $photo->thumbnail_path : null,
            ]));
            if (! empty($paths)) {
                Storage::disk($fallbackDisk)->delete($paths);
            }
        }

        $eventPackage = $event->eventPackage;
        $usageTracker = app(\App\Services\Packages\PackageUsageTracker::class);

        // Delete record and likes
        DB::transaction(function () use ($photo, $assets) {
            $photo->likes()->delete();
            if ($assets->isNotEmpty()) {
                EventMediaAsset::whereIn('id', $assets->pluck('id'))->delete();
            }
            $photo->delete();
        });

        if ($eventPackage && $eventPackage->package) {
            $previousUsed = (int) $eventPackage->used_photos;
            if ($previousUsed > 0) {
                $eventPackage->decrement('used_photos');
                $eventPackage->refresh();
                $usageTracker->recordPhotoUsage($eventPackage, $previousUsed, -1);
            }
        }

        return response()->json([
            'message' => 'Photo deleted successfully',
        ]);
    }

    /**
     * Bulk approve photos (admin only)
     */
    public function feature(Request $request, string $eventSlug, Photo $photo): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();
        TenantMemberPermissions::ensureEventPermission($request, $event, 'photos:moderate');

        if ($photo->event_id !== $event->id) {
            return ApiError::response(
                'photo_not_found',
                'Photo not found',
                'The specified photo could not be located for this event.',
                Response::HTTP_NOT_FOUND,
                ['photo_id' => $photo->id, 'event_id' => $event->id]
            );
        }

        $photo->update(['is_featured' => true]);
        $photo->refresh()->load('event')->loadCount('likes');

        return response()->json(['message' => 'Photo marked as featured', 'data' => new PhotoResource($photo)]);
    }

    public function unfeature(Request $request, string $eventSlug, Photo $photo): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();
        TenantMemberPermissions::ensureEventPermission($request, $event, 'photos:moderate');

        if ($photo->event_id !== $event->id) {
            return ApiError::response(
                'photo_not_found',
                'Photo not found',
                'The specified photo could not be located for this event.',
                Response::HTTP_NOT_FOUND,
                ['photo_id' => $photo->id, 'event_id' => $event->id]
            );
        }

        $photo->update(['is_featured' => false]);
        $photo->refresh()->load('event')->loadCount('likes');

        return response()->json(['message' => 'Photo removed from featured', 'data' => new PhotoResource($photo)]);
    }

    public function bulkApprove(Request $request, string $eventSlug): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();
        TenantMemberPermissions::ensureEventPermission($request, $event, 'photos:moderate');

        $request->validate([
            'photo_ids' => 'required|array',
            'photo_ids.*' => 'exists:photos,id',
            'moderation_notes' => 'nullable|string|max:1000',
        ]);

        $photoIds = $request->photo_ids;
        $approvedCount = Photo::whereIn('id', $photoIds)
            ->where('event_id', $event->id)
            ->where('status', 'pending')
            ->update([
                'status' => 'approved',
                'moderation_notes' => $request->moderation_notes,
                'moderated_at' => now(),
                'moderated_by' => null,
            ]);

        // Load approved photos for response
        $photos = Photo::whereIn('id', $photoIds)
            ->where('event_id', $event->id)
            ->with('event')->withCount('likes')
            ->get();

        // Trigger events
        foreach ($photos as $photo) {
            // event(new \App\Events\PhotoApproved($photo)); // Implement later
        }

        return response()->json([
            'message' => "{$approvedCount} photos approved successfully",
            'approved_count' => $approvedCount,
            'data' => PhotoResource::collection($photos),
        ]);
    }

    /**
     * Bulk reject photos (admin only)
     */
    public function bulkReject(Request $request, string $eventSlug): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();
        TenantMemberPermissions::ensureEventPermission($request, $event, 'photos:moderate');

        $request->validate([
            'photo_ids' => 'required|array',
            'photo_ids.*' => 'exists:photos,id',
            'moderation_notes' => 'required|string|max:1000',
        ]);

        $photoIds = $request->photo_ids;
        $rejectedCount = Photo::whereIn('id', $photoIds)
            ->where('event_id', $event->id)
            ->where('status', 'pending')
            ->update([
                'status' => 'rejected',
                'moderation_notes' => $request->moderation_notes,
                'moderated_at' => now(),
                'moderated_by' => null,
            ]);

        // Optionally delete rejected photos from storage
        $rejectedPhotos = Photo::whereIn('id', $photoIds)
            ->where('event_id', $event->id)
            ->get();

        foreach ($rejectedPhotos as $photo) {
            Storage::disk('public')->delete([
                $photo->path,
                $photo->thumbnail_path,
            ]);
        }

        return response()->json([
            'message' => "{$rejectedCount} photos rejected and deleted",
            'rejected_count' => $rejectedCount,
        ]);
    }

    /**
     * Get photos for moderation (admin only)
     */
    public function forModeration(Request $request, string $eventSlug): AnonymousResourceCollection
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

        $photos = Photo::where('event_id', $event->id)
            ->where('status', 'pending')
            ->with('event')->withCount('likes')
            ->orderBy('created_at', 'desc')
            ->paginate($request->get('per_page', 20));

        return PhotoResource::collection($photos);
    }

    /**
     * Get upload statistics for event
     */
    public function stats(Request $request, string $eventSlug): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

        $totalPhotos = Photo::where('event_id', $event->id)->count();
        $pendingPhotos = Photo::where('event_id', $event->id)->where('status', 'pending')->count();
        $approvedPhotos = Photo::where('event_id', $event->id)->where('status', 'approved')->count();
        $totalLikes = DB::table('photo_likes')->whereIn('photo_id',
            Photo::where('event_id', $event->id)->pluck('id')
        )->count();
        $totalStorage = Photo::where('event_id', $event->id)->sum('size');
        $uniqueUploaders = Photo::where('event_id', $event->id)
            ->select('uploader_id')
            ->distinct()
            ->count('uploader_id');

        $recentUploads = Photo::where('event_id', $event->id)
            ->where('created_at', '>=', now()->subDays(7))
            ->count();

        return response()->json([
            'event_id' => $event->id,
            'total_photos' => $totalPhotos,
            'pending_photos' => $pendingPhotos,
            'approved_photos' => $approvedPhotos,
            'total_likes' => $totalLikes,
            'total_storage_bytes' => $totalStorage,
            'total_storage_mb' => round($totalStorage / (1024 * 1024), 2),
            'unique_uploaders' => $uniqueUploaders,
            'recent_uploads_7d' => $recentUploads,
            'storage_quota_remaining' => $event->tenant->storage_quota - $totalStorage,
            'quota_percentage_used' => min(100, round(($totalStorage / $event->tenant->storage_quota) * 100, 1)),
        ]);
    }

    private function tokenHasScope(Request $request, string $scope): bool
    {
        $accessToken = $request->user()?->currentAccessToken();
        if ($accessToken && $accessToken->can($scope)) {
            return true;
        }

        $scopes = $request->user()->scopes ?? ($request->attributes->get('decoded_token')['scopes'] ?? []);

        if (! is_array($scopes)) {
            $scopes = array_values(array_filter(explode(' ', (string) $scopes)));
        }

        return in_array($scope, $scopes, true);
    }

    /**
     * Generate presigned S3 URL for direct upload (alternative to local storage)
     */
    public function presignedUpload(Request $request, string $eventSlug): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

        $request->validate([
            'filename' => 'required|string|max:255',
            'content_type' => 'required|string|in:image/jpeg,image/png,image/webp',
        ]);

        // Generate unique filename
        $extension = pathinfo($request->filename, PATHINFO_EXTENSION);
        $filename = Str::uuid().'.'.$extension;
        $path = "events/{$eventSlug}/pending/{$filename}";

        // For local storage, return direct upload endpoint
        // For S3, use Storage::disk('s3')->temporaryUrl() or presigned URL
        $uploadUrl = url("/api/v1/tenant/events/{$eventSlug}/upload-direct");

        $fields = [
            'event_id' => $event->id,
            'filename' => $filename,
            'original_name' => $request->filename,
        ];

        return response()->json([
            'upload_url' => $uploadUrl,
            'fields' => $fields,
            'path' => $path,
            'max_size' => 10 * 1024 * 1024, // 10MB
            'allowed_types' => ['image/jpeg', 'image/png', 'image/webp'],
        ]);
    }

    /**
     * Direct upload endpoint for presigned uploads
     */
    public function uploadDirect(Request $request, string $eventSlug): JsonResponse
    {
        $tenantId = $request->attributes->get('tenant_id');
        $event = Event::where('slug', $eventSlug)
            ->where('tenant_id', $tenantId)
            ->firstOrFail();

        $request->validate([
            'event_id' => 'required|exists:events,id',
            'filename' => 'required|string',
            'original_name' => 'required|string',
            'photo' => 'required|image|mimes:jpeg,png,webp|max:10240', // 10MB
        ]);

        if ($request->event_id !== $event->id) {
            return ApiError::response(
                'event_mismatch',
                'Invalid Event',
                'The provided event does not match the authenticated tenant event.',
                Response::HTTP_BAD_REQUEST,
                ['payload_event_id' => $request->event_id, 'expected_event_id' => $event->id]
            );
        }

        $event->load('storageAssignments.storageTarget');
        $disk = $this->eventStorageManager->getHotDiskForEvent($event);

        $file = $request->file('photo');
        $filename = $request->filename;
        $path = "events/{$eventSlug}/photos/{$filename}";

        // Store file
        UploadStream::putUploadedFile($disk, $path, $file);

        // Generate thumbnail
        $thumbnailPath = "events/{$eventSlug}/thumbnails/{$filename}";
        $thumbnailRelative = ImageHelper::makeThumbnailOnDisk($disk, $path, $thumbnailPath, 400);
        if ($thumbnailRelative) {
            $thumbnailPath = $thumbnailRelative;
        }

        $photoAttributes = [
            'event_id' => $event->id,
            'guest_name' => Photo::SOURCE_TENANT_ADMIN,
            'original_name' => $request->original_name,
            'mime_type' => $file->getMimeType(),
            'size' => $file->getSize(),
            'file_path' => $path,
            'thumbnail_path' => $thumbnailPath,
            'status' => 'pending',
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'ingest_source' => Photo::SOURCE_TENANT_ADMIN,
        ];

        if (Photo::supportsFilenameColumn()) {
            $photoAttributes['filename'] = $filename;
        }
        if (Photo::hasColumn('path')) {
            $photoAttributes['path'] = $path;
        }

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

        [$width, $height] = getimagesize($file->getRealPath());
        $photo->update(['width' => $width, 'height' => $height]);

        $checksum = hash_file('sha256', $file->getRealPath());
        $asset = $this->eventStorageManager->recordAsset($event, $disk, $path, [
            'variant' => 'original',
            'mime_type' => $file->getMimeType(),
            'size_bytes' => $file->getSize(),
            'checksum' => $checksum,
            'status' => 'hot',
            'processed_at' => now(),
            'photo_id' => $photo->id,
        ]);

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

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

        return response()->json([
            'message' => 'Upload successful. Awaiting moderation.',
            'photo_id' => $photo->id,
            'status' => 'pending',
        ], 201);
    }

    private function resolvePhotoVariant(Photo $record, string $variant, bool $preferOriginals = false): array
    {
        if ($variant === 'thumbnail') {
            $asset = EventMediaAsset::where('photo_id', $record->id)->where('variant', 'thumbnail')->first();
            $watermarked = $preferOriginals
                ? null
                : EventMediaAsset::where('photo_id', $record->id)->where('variant', 'watermarked_thumbnail')->first();
            $disk = $asset?->disk ?? $record->mediaAsset?->disk;
            $path = $watermarked?->path ?? $asset?->path ?? ($record->thumbnail_path ?: $record->file_path);
            $mime = $watermarked?->mime_type ?? $asset?->mime_type ?? 'image/jpeg';
        } else {
            $watermarked = $preferOriginals
                ? null
                : EventMediaAsset::where('photo_id', $record->id)->where('variant', 'watermarked')->first();
            $asset = $record->mediaAsset ?? $watermarked ?? EventMediaAsset::where('photo_id', $record->id)->where('variant', 'original')->first();
            $disk = $asset?->disk ?? $record->mediaAsset?->disk;
            $path = $watermarked?->path ?? $asset?->path ?? ($record->file_path ?? null);
            $mime = $watermarked?->mime_type ?? $asset?->mime_type ?? ($record->mime_type ?? 'image/jpeg');
        }

        return [
            $disk ?: config('filesystems.default', 'public'),
            $path,
            $mime,
        ];
    }

    private function storagePathCandidates(string $path): array
    {
        $normalized = str_replace('\\', '/', $path);
        $candidates = [$normalized];

        $trimmed = ltrim($normalized, '/');
        if ($trimmed !== $normalized) {
            $candidates[] = $trimmed;
        }

        if (str_starts_with($trimmed, 'storage/')) {
            $candidates[] = substr($trimmed, strlen('storage/'));
        }

        if (str_starts_with($trimmed, 'public/')) {
            $candidates[] = substr($trimmed, strlen('public/'));
        }

        $needle = '/storage/app/public/';
        if (str_contains($normalized, $needle)) {
            $candidates[] = substr($normalized, strpos($normalized, $needle) + strlen($needle));
        }

        return array_values(array_unique(array_filter($candidates)));
    }
}
