<?php

namespace App\Http\Controllers;

use App\Models\Slide;
use App\Services\FileConversionService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;
use InvalidArgumentException;

class SlideController extends Controller
{
    private const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg'];

    private const POWERPOINT_EXTENSIONS = ['ppt', 'pptx'];

    public function store(Request $request, FileConversionService $conversionService): JsonResponse
    {
        $validated = $request->validate([
            'file' => ['required', 'file', 'max:51200'],
            'type' => ['required', Rule::in(['information', 'moderation', 'sermon', 'agenda_item'])],
            'service_id' => ['nullable', 'exists:services,id'],
            'service_agenda_item_id' => ['nullable', 'integer', 'exists:service_agenda_items,id'],
            'expire_date' => ['nullable', 'date'],
        ]);

        // moderation, sermon, and agenda_item slides require a service_id
        if (in_array($validated['type'], ['moderation', 'sermon', 'agenda_item']) && empty($validated['service_id'])) {
            return response()->json([
                'message' => 'Moderations- und Predigtfolien benötigen einen Gottesdienst.',
                'errors' => ['service_id' => ['Gottesdienst ist erforderlich.']],
            ], 422);
        }

        /** @var UploadedFile $file */
        $file = $request->file('file');
        $extension = strtolower($file->getClientOriginalExtension());

        // Validate file extension
        $allowedExtensions = [...self::IMAGE_EXTENSIONS, ...self::POWERPOINT_EXTENSIONS, 'zip'];
        if (! in_array($extension, $allowedExtensions, true)) {
            return response()->json([
                'message' => 'Dateityp nicht erlaubt.',
                'errors' => ['file' => ['Nur PNG, JPG, PPT, PPTX und ZIP-Dateien sind erlaubt.']],
            ], 422);
        }

        $uploaderName = $request->user()?->name ?? 'Unbekannt';
        $serviceId = $validated['service_id'] ?? null;
        $serviceAgendaItemId = $validated['service_agenda_item_id'] ?? null;
        $type = $validated['type'];
        $expireDate = $validated['expire_date'] ?? null;

        // Handle PowerPoint files — dispatch async job
        if (in_array($extension, self::POWERPOINT_EXTENSIONS, true)) {
            return $this->handlePowerPoint($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate, $serviceAgendaItemId);
        }

        // Handle ZIP files — extract and process
        if ($extension === 'zip') {
            return $this->handleZip($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate, $serviceAgendaItemId);
        }

        // Handle images — convert synchronously
        return $this->handleImage($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate, $serviceAgendaItemId);
    }

    public function destroy(Slide $slide): JsonResponse
    {
        $slide->delete();

        return response()->json([
            'success' => true,
            'message' => 'Folie wurde gelöscht.',
        ]);
    }

    public function destroyBulk(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'type' => ['required', Rule::in(['information', 'moderation', 'sermon', 'agenda_item'])],
            'service_id' => ['nullable', 'exists:services,id'],
        ]);

        $query = Slide::where('type', $validated['type']);

        if ($validated['service_id']) {
            $query->where('service_id', $validated['service_id']);
        } else {
            // Information slides without service_id (global)
            $query->whereNull('service_id');
        }

        $count = $query->count();
        $query->delete(); // soft-delete via SoftDeletes trait

        return response()->json([
            'success' => true,
            'message' => $count.' Folien wurden gelöscht.',
            'count' => $count,
        ]);
    }

    public function reorder(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'slides' => ['required', 'array', 'min:1'],
            'slides.*.id' => ['required', 'integer', 'exists:slides,id'],
            'slides.*.sort_order' => ['required', 'integer', 'min:0'],
        ]);

        foreach ($validated['slides'] as $item) {
            Slide::where('id', $item['id'])->update(['sort_order' => $item['sort_order']]);
        }

        return response()->json([
            'success' => true,
            'message' => 'Reihenfolge wurde aktualisiert.',
        ]);
    }

    public function updateExpireDate(Request $request, Slide $slide): JsonResponse
    {
        if ($slide->type !== 'information') {
            return response()->json([
                'message' => 'Ablaufdatum kann nur für Informationsfolien gesetzt werden.',
                'errors' => ['type' => ['Nur Informationsfolien haben ein Ablaufdatum.']],
            ], 422);
        }

        $validated = $request->validate([
            'expire_date' => ['nullable', 'date'],
        ]);

        $slide->update(['expire_date' => $validated['expire_date']]);

        return response()->json([
            'success' => true,
            'slide' => $slide->fresh(),
        ]);
    }

    private function nextSortOrder(string $type, ?int $serviceId): int
    {
        return (int) Slide::where('type', $type)
            ->when($serviceId, fn ($q) => $q->where('service_id', $serviceId), fn ($q) => $q->whereNull('service_id'))
            ->max('sort_order') + 1;
    }

    private function handleImage(
        UploadedFile $file,
        FileConversionService $conversionService,
        string $type,
        ?int $serviceId,
        string $uploaderName,
        ?string $expireDate,
        ?int $serviceAgendaItemId = null,
    ): JsonResponse {
        try {
            $result = $conversionService->convertImage($file);

            $slide = Slide::create([
                'type' => $type,
                'service_id' => $serviceId,
                'service_agenda_item_id' => $serviceAgendaItemId,
                'original_filename' => $file->getClientOriginalName(),
                'stored_filename' => $result['filename'],
                'thumbnail_filename' => $result['thumbnail'],
                'cover_mode' => $result['fullCover'] ?? null,
                'expire_date' => $expireDate,
                'uploader_name' => $uploaderName,
                'uploaded_at' => now(),
                'sort_order' => $this->nextSortOrder($type, $serviceId),
            ]);

            $response = [
                'success' => true,
                'slide' => $slide,
            ];

            if (! empty($result['warnings'])) {
                $response['warnings'] = $result['warnings'];
            }

            return response()->json($response);
        } catch (InvalidArgumentException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        }
    }

    private function handlePowerPoint(
        UploadedFile $file,
        FileConversionService $conversionService,
        string $type,
        ?int $serviceId,
        string $uploaderName,
        ?string $expireDate,
        ?int $serviceAgendaItemId = null,
    ): JsonResponse {
        // Store file persistently so the job can access it
        $storedPath = $file->store('temp/ppt', 'local');

        try {
            $jobId = $conversionService->convertPowerPoint(
                storage_path('app/'.$storedPath)
            );

            return response()->json([
                'success' => true,
                'job_id' => $jobId,
                'message' => 'PowerPoint wird verarbeitet...',
                'meta' => [
                    'type' => $type,
                    'service_id' => $serviceId,
                    'service_agenda_item_id' => $serviceAgendaItemId,
                    'uploader_name' => $uploaderName,
                    'expire_date' => $expireDate,
                    'original_filename' => $file->getClientOriginalName(),
                ],
            ]);
        } catch (InvalidArgumentException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        }
    }

    private function handleZip(
        UploadedFile $file,
        FileConversionService $conversionService,
        string $type,
        ?int $serviceId,
        string $uploaderName,
        ?string $expireDate,
        ?int $serviceAgendaItemId = null,
    ): JsonResponse {
        try {
            $results = $conversionService->processZip($file);
            $slides = [];
            $allWarnings = [];

            $sortOrder = $this->nextSortOrder($type, $serviceId);

            foreach ($results as $result) {
                // Skip PPT job results (they are handled asynchronously)
                if (isset($result['job_id'])) {
                    continue;
                }

                $slides[] = Slide::create([
                    'type' => $type,
                    'service_id' => $serviceId,
                    'service_agenda_item_id' => $serviceAgendaItemId,
                    'original_filename' => $file->getClientOriginalName(),
                    'stored_filename' => $result['filename'],
                    'thumbnail_filename' => $result['thumbnail'],
                    'cover_mode' => $result['fullCover'] ?? null,
                    'expire_date' => $expireDate,
                    'uploader_name' => $uploaderName,
                    'uploaded_at' => now(),
                    'sort_order' => $sortOrder++,
                ]);

                if (! empty($result['warnings'])) {
                    array_push($allWarnings, ...$result['warnings']);
                }
            }

            $response = [
                'success' => true,
                'slides' => $slides,
                'count' => count($slides),
            ];

            if (! empty($allWarnings)) {
                $response['warnings'] = array_values(array_unique($allWarnings));
            }

            return response()->json($response);
        } catch (InvalidArgumentException $e) {
            Log::warning('ZIP-Verarbeitung fehlgeschlagen', ['error' => $e->getMessage()]);

            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        }
    }
}
