<?php

declare(strict_types=1);

namespace App\Framework\Database\Repository;

use App\Framework\Database\EntityManager;

/**
 * Base Repository Service für Entities (Komposition statt Vererbung)
 * Wird als Dependency in Domain Repositories injiziert
 */
final readonly class EntityRepository
{
    private BatchLoader $batchLoader;

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

    /**
     * Findet Entity nach ID
     */
    public function find(string $entityClass, string $id): ?object
    {
        return $this->entityManager->find($entityClass, $id);
    }

    /**
     * Findet Entity nach ID (eager loading)
     */
    public function findEager(string $entityClass, string $id): ?object
    {
        return $this->entityManager->findEager($entityClass, $id);
    }

    /**
     * Findet alle Entities
     * @return array<int, object>
     */
    public function findAll(string $entityClass): array
    {
        return $this->entityManager->findAll($entityClass);
    }

    /**
     * Findet alle Entities (eager loading)
     * @return array<int, object>
     */
    public function findAllEager(string $entityClass): array
    {
        return $this->entityManager->findAllEager($entityClass);
    }

    /**
     * Findet Entities nach Kriterien
     * @param array<string, mixed> $criteria
     * @param array<string, string>|null $orderBy
     * @return array<int, object>
     */
    public function findBy(string $entityClass, array $criteria, ?array $orderBy = null, ?int $limit = null): array
    {
        return $this->entityManager->findBy($entityClass, $criteria, $orderBy, $limit);
    }

    /**
     * Findet eine Entity nach Kriterien
     * @param array<string, mixed> $criteria
     */
    public function findOneBy(string $entityClass, array $criteria): ?object
    {
        return $this->entityManager->findOneBy($entityClass, $criteria);
    }

    /**
     * Speichert eine Entity
     */
    public function save(object $entity): object
    {
        return $this->entityManager->save($entity);
    }

    /**
     * Speichert mehrere Entities
     * @param array<int, object> $entities
     * @return array<int, object>
     */
    public function saveAll(array $entities): array
    {
        return $this->entityManager->saveAll(...$entities);
    }

    /**
     * Löscht eine Entity
     */
    public function delete(object $entity): void
    {
        $this->entityManager->delete($entity);
    }

    /**
     * Führt eine Funktion in einer Transaktion aus
     */
    public function transaction(callable $callback): mixed
    {
        return $this->entityManager->transaction($callback);
    }

    /**
     * Findet Entities mit Batch Loading von Relations (verhindert N+1 Queries)
     *
     * @param string $entityClass
     * @param array<string, mixed> $criteria
     * @param array<string> $relations Relations die vorgeladen werden sollen
     * @param array<string, string>|null $orderBy
     * @param int|null $limit
     * @return array<int, object>
     */
    public function findWithRelations(
        string $entityClass,
        array $criteria = [],
        array $relations = [],
        ?array $orderBy = null,
        ?int $limit = null
    ): array {
        return $this->batchLoader->findWithRelations(
            $entityClass,
            $criteria,
            $relations,
            $orderBy,
            $limit
        );
    }

    /**
     * Findet paginierte Entities mit optionalem Batch Loading
     *
     * @param string $entityClass
     * @param int $page Aktuelle Seite (1-basiert)
     * @param int $limit Items pro Seite
     * @param array<string, mixed> $criteria
     * @param array<string> $relations Relations die vorgeladen werden sollen
     * @param array<string, string>|null $orderBy
     * @return PaginatedResult
     */
    public function findPaginated(
        string $entityClass,
        int $page = 1,
        int $limit = 20,
        array $criteria = [],
        array $relations = [],
        ?array $orderBy = null
    ): PaginatedResult {
        return $this->batchLoader->findPaginated(
            $entityClass,
            $page,
            $limit,
            $criteria,
            $relations,
            $orderBy
        );
    }

    /**
     * Zählt Entities nach Kriterien
     *
     * @param string $entityClass
     * @param array<string, mixed> $criteria
     * @return int
     */
    public function countBy(string $entityClass, array $criteria = []): int
    {
        return $this->batchLoader->countBy($entityClass, $criteria);
    }

    /**
     * Batch-Save für mehrere Entities mit optimierter Performance
     *
     * @param array<int, object> $entities
     * @param int $batchSize Anzahl Entities pro Batch
     * @return array<int, object>
     */
    public function saveBatch(array $entities, int $batchSize = 100): array
    {
        return $this->batchLoader->saveBatch($entities, $batchSize);
    }

    /**
     * Batch-Delete für mehrere Entities
     *
     * @param array<int, object> $entities
     * @param int $batchSize Anzahl Entities pro Batch
     */
    public function deleteBatch(array $entities, int $batchSize = 100): void
    {
        $this->batchLoader->deleteBatch($entities, $batchSize);
    }

    /**
     * Gibt den BatchLoader für erweiterte Operationen zurück
     */
    public function getBatchLoader(): BatchLoader
    {
        return $this->batchLoader;
    }
}
