<?php

declare(strict_types=1);

namespace App\Domain\Media;

use App\Framework\Database\EntityManager;
use App\Framework\Database\Repository\BatchLoader;
use App\Framework\Database\Repository\PaginatedResult;

/**
 * Repository für Image Entities mit optimiertem Batch Loading
 * Nutzt Komposition statt Vererbung gemäß Framework-Prinzipien
 */
final readonly class ImageRepository
{
    private BatchLoader $batchLoader;

    public function __construct(
        private EntityManager $entityManager
    ) {
        $this->batchLoader = new BatchLoader($entityManager);
    }

    /**
     * Speichert ein Bild mit Datei
     */
    public function save(Image $image, string $tempPath): void
    {
        new SaveImageFile()($image, $tempPath);
        $this->entityManager->save($image);
    }

    /**
     * Findet Bild durch Slot Name (mit Batch Loading)
     */
    public function findBySlotName(string $slotName): ?Image
    {
        $slot = $this->entityManager->findOneBy(ImageSlot::class, ['slot_name' => $slotName]);

        if (! $slot) {
            return null;
        }

        // Nutze Batch Loading um N+1 zu vermeiden
        $images = $this->batchLoader->findWithRelations(
            Image::class,
            criteria: ['slot_id' => $slot->id],
            relations: ['slot', 'variants']
        );

        return $images[0] ?? null;
    }

    /**
     * Findet Bild durch ID mit optionalen Relations
     */
    public function findById(string $id, array $relations = []): ?Image
    {
        if (! empty($relations)) {
            $images = $this->batchLoader->findWithRelations(
                Image::class,
                criteria: ['id' => $id],
                relations: $relations
            );

            return $images[0] ?? null;
        }

        return $this->entityManager->find(Image::class, $id);
    }

    /**
     * Findet Bild durch Filename
     */
    public function findByFilename(string $filename): ?Image
    {
        return $this->entityManager->findOneBy(Image::class, ['filename' => $filename]);
    }

    /**
     * Findet Bild durch Hash
     */
    public function findByHash(string $hash): ?Image
    {
        return $this->entityManager->findOneBy(Image::class, ['hash' => $hash]);
    }

    /**
     * Findet Bild durch ULID
     */
    public function findByUlid(string $ulid): ?Image
    {
        return $this->entityManager->find(Image::class, $ulid);
    }

    /**
     * Findet alle Bilder paginiert mit optionaler Suche
     *
     * @return PaginatedResult
     */
    public function findAllPaginated(
        int $page = 1,
        int $limit = 50,
        ?string $search = null,
        array $relations = []
    ): PaginatedResult {
        $criteria = [];

        // TODO: Implementiere Volltext-Suche auf DB-Ebene
        // Aktuell müssen wir noch manuell filtern für Suche
        if ($search !== null) {
            // Temporäre Lösung: Lade alle und filtere
            // Dies sollte durch eine richtige DB-Query ersetzt werden
            $allImages = $this->batchLoader->findWithRelations(
                Image::class,
                [],
                $relations
            );

            $filtered = array_filter($allImages, function (Image $image) use ($search) {
                return stripos($image->originalFilename, $search) !== false ||
                       stripos($image->altText ?? '', $search) !== false;
            });

            $totalItems = count($filtered);
            $offset = ($page - 1) * $limit;
            $items = array_slice($filtered, $offset, $limit);

            return PaginatedResult::fromQuery($items, $totalItems, $page, $limit);
        }

        // Ohne Suche: Nutze optimierte Pagination
        return $this->batchLoader->findPaginated(
            Image::class,
            $page,
            $limit,
            $criteria,
            $relations
        );
    }

    /**
     * Findet alle Bilder (Legacy-Methode für Kompatibilität)
     *
     * @deprecated Nutze findAllPaginated stattdessen
     * @return Image[]
     */
    public function findAll(int $limit = 50, int $offset = 0, ?string $search = null): array
    {
        $page = (int) floor($offset / $limit) + 1;
        $result = $this->findAllPaginated($page, $limit, $search);

        return $result->items;
    }

    /**
     * Zählt Bilder mit optionaler Suche
     */
    public function count(?string $search = null): int
    {
        if ($search !== null) {
            // Temporäre Lösung für Suche
            $allImages = $this->entityManager->findAll(Image::class);

            $filtered = array_filter($allImages, function (Image $image) use ($search) {
                return stripos($image->originalFilename, $search) !== false ||
                       stripos($image->altText ?? '', $search) !== false;
            });

            return count($filtered);
        }

        return $this->batchLoader->countBy(Image::class, []);
    }

    /**
     * Aktualisiert Alt-Text eines Bildes
     */
    public function updateAltText(string $ulid, string $altText): void
    {
        $image = $this->findByUlid($ulid);

        if ($image) {
            // Verwende withAltText wenn verfügbar, sonst direkte Zuweisung
            if (method_exists($image, 'withAltText')) {
                $updatedImage = $image->withAltText($altText);
            } else {
                $image->altText = $altText;
                $updatedImage = $image;
            }

            $this->entityManager->save($updatedImage);
        }
    }

    /**
     * Aktualisiert Filename eines Bildes
     */
    public function updateFilename(string $ulid, string $filename): void
    {
        $image = $this->findByUlid($ulid);

        if ($image) {
            $updatedImage = $image->withFilename($filename);
            $this->entityManager->save($updatedImage);
        }
    }

    /**
     * Erweiterte Bildsuche mit Filtern
     *
     * @return Image[]
     */
    public function search(
        string $query = '',
        ?string $type = null,
        int $minWidth = 0,
        int $minHeight = 0,
        int $limit = 100
    ): array {
        // TODO: Implementiere erweiterte Suche auf DB-Ebene mit QueryBuilder
        // Aktuell: Temporäre Lösung mit Array-Filterung

        $allImages = $this->batchLoader->findWithRelations(
            Image::class,
            [],
            ['variants']
        );

        $filteredImages = array_filter($allImages, function (Image $image) use ($query, $type, $minWidth, $minHeight) {
            // Text search
            if ($query && ! (
                stripos($image->originalFilename, $query) !== false ||
                stripos($image->altText ?? '', $query) !== false
            )) {
                return false;
            }

            // Type filter
            if ($type && $image->mimeType !== 'image/' . $type) {
                return false;
            }

            // Size filters
            if ($minWidth > 0 && $image->width < $minWidth) {
                return false;
            }

            if ($minHeight > 0 && $image->height < $minHeight) {
                return false;
            }

            return true;
        });

        return array_slice($filteredImages, 0, $limit);
    }

    /**
     * Findet Bilder mit ihren Slots (optimiert für Gallery Views)
     *
     * @return Image[]
     */
    public function findAllWithSlots(int $limit = 50): array
    {
        return $this->batchLoader->findWithRelations(
            Image::class,
            criteria: [],
            relations: ['slot', 'variants'],
            limit: $limit
        );
    }

    /**
     * Batch-Update für mehrere Bilder
     *
     * @param Image[] $images
     * @return Image[]
     */
    public function updateBatch(array $images): array
    {
        return $this->batchLoader->saveBatch($images);
    }

    /**
     * Lädt Bilder mit allen Relations für Export
     *
     * @return Image[]
     */
    public function findForExport(array $imageIds): array
    {
        return $this->batchLoader->findWithRelations(
            Image::class,
            criteria: ['id' => $imageIds],
            relations: ['slot', 'variants', 'metadata']
        );
    }

    /**
     * Löscht mehrere Bilder im Batch
     *
     * @param Image[] $images
     */
    public function deleteBatch(array $images): void
    {
        $this->batchLoader->deleteBatch($images);
    }
}
