<?php

namespace App\Http\Controllers\Tenant;

use App\Http\Controllers\Controller;
use App\Models\Event;
use App\Models\EventMediaAsset;
use App\Models\Photo;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
use ZipArchive;

class EventPhotoArchiveController extends Controller
{
    public function __invoke(Request $request, Event $event): StreamedResponse
    {
        $user = Auth::user();

        if (! $user || (int) $user->tenant_id !== (int) $event->tenant_id) {
            abort(403, 'Unauthorized');
        }

        $photos = Photo::query()
            ->with('mediaAsset')
            ->where('event_id', $event->id)
            ->where('status', 'approved')
            ->orderBy('created_at')
            ->get();

        if ($photos->isEmpty()) {
            abort(404, 'No approved photos available for this event.');
        }

        $zip = new ZipArchive();
        $tempPath = tempnam(sys_get_temp_dir(), 'fotospiel-photos-');

        if ($tempPath === false || $zip->open($tempPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            abort(500, 'Unable to generate archive.');
        }

        foreach ($photos as $photo) {
            $filename = $this->buildFilename($event, $photo);

            $asset = $photo->mediaAsset ?? EventMediaAsset::query()
                ->where('photo_id', $photo->id)
                ->where('variant', 'original')
                ->first();

            $added = false;

            if ($asset && $asset->path) {
                $added = $this->addDiskFileToArchive($zip, $asset->disk ?? config('filesystems.default'), $asset->path, $filename);
            }

            if (! $added && $photo->file_path) {
                $added = $this->addDiskFileToArchive($zip, config('filesystems.default'), $photo->file_path, $filename);
            }

            if (! $added) {
                Log::warning('Skipping photo in archive build', [
                    'event_id' => $event->id,
                    'photo_id' => $photo->id,
                ]);
            }
        }

        $zip->close();

        $downloadName = sprintf('fotospiel-event-%s-photos.zip', $event->slug ?? $event->id);

        return response()->streamDownload(function () use ($tempPath) {
            $stream = fopen($tempPath, 'rb');

            if (! $stream) {
                throw new FileNotFoundException('Archive could not be opened.');
            }

            fpassthru($stream);
            fclose($stream);
            unlink($tempPath);
        }, $downloadName, [
            'Content-Type' => 'application/zip',
        ]);
    }

    private function buildFilename(Event $event, Photo $photo): string
    {
        $timestamp = $photo->created_at?->format('Ymd_His') ?? now()->format('Ymd_His');
        $extension = pathinfo($photo->file_path ?? '', PATHINFO_EXTENSION) ?: 'jpg';

        return sprintf('%s-photo-%d.%s', $timestamp, $photo->id, $extension);
    }

    private function addDiskFileToArchive(ZipArchive $zip, ?string $diskName, string $path, string $filename): bool
    {
        $disk = $diskName ? Storage::disk($diskName) : Storage::disk(config('filesystems.default'));

        try {
            if (method_exists($disk, 'path')) {
                $absolute = $disk->path($path);

                if (is_file($absolute)) {
                    return $zip->addFile($absolute, $filename);
                }
            }

            $stream = $disk->readStream($path);

            if ($stream) {
                $contents = stream_get_contents($stream);
                fclose($stream);

                if ($contents !== false) {
                    return $zip->addFromString($filename, $contents);
                }
            }
        } catch (\Throwable $e) {
            Log::warning('Failed to add file to archive', [
                'disk' => $diskName,
                'path' => $path,
                'error' => $e->getMessage(),
            ]);
        }

        return false;
    }
}

