<?php

namespace AutoAdmin\Bundle\Media\Library;

use AutoAdmin\Bundle\Content\Model\CreatedAtInterface;
use AutoAdmin\Bundle\Content\Model\UpdatedAtInterface;
use AutoAdmin\Bundle\Content\Repository\AbstractTranslatableContentRepository;
use AutoAdmin\Bundle\Content\Site\SiteContextHelper;
use AutoAdmin\Bundle\Core\Util\SizeConverter;
use AutoAdmin\Bundle\Media\Model\Library\BaseMediaInterface;
use AutoAdmin\Bundle\Media\Model\Library\LibraryItem;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr;
use RuntimeException;

class MediaLibrary
{
    const PER_PAGE = 24;

    public function __construct(
        protected SiteContextHelper $contextHelper,
        protected MediaTypeResolver $typeResolver,
        protected MediaPathGenerator $urlGenerator,
        protected EntityManagerInterface $em,
        protected SizeConverter $sizeConverter,
    ) {
    }

    public function browse(array $types, int $page, string $searchTerm = null): array
    {
        $results = [];

        foreach ($types as $type) {
            if (!in_array($type, $this->typeResolver->getAllSlugs())) {
                throw new RuntimeException('Unsupported media type: '.$type);
            }
            $results = [
                ...$results,
                ...$this->browseType($type, $page, $searchTerm),
            ];
        }

        $results = $this->orderMixedResults($results);
        $results = $this->addInfos($results);

        return $results;
    }

    public function browseCount(array $types, int $page, string $searchTerm = null)
    {
        $count = 0;

        foreach ($types as $type) {
            if (!in_array($type, $this->typeResolver->getAllSlugs())) {
                throw new RuntimeException('Unsupported media type: '.$type);
            }
            $count += $this->browseTypeCount($type, $page, $searchTerm);
        }

        return $count;
    }

    public function get(string $typeSlug, int $id): ?BaseMediaInterface
    {
        $repo = $this->getRepo($typeSlug);
        $media = $repo->find($id);

        return $media;
    }


    private function prepareBrowseCriteria(string $type, int $page, $searchTerm = null): array
    {
        $findBy = [];

        if ($searchTerm) {
            $searchTermSplit = explode(',', $searchTerm);
            $orX = (new Expr())->orX();
            foreach ($searchTermSplit as $i => $split) {
                $findBy['aa_parameters']['searchTerm'.$i] = '%'.$split.'%';
                $splitOrX = (new Expr())->orX();
                if ($type === 'image') {
                    $splitOrX->add('_tr.alt LIKE :searchTerm'.$i);
                }
                if ($type === 'file' || 'image') {
                    $splitOrX->add('_.originalName LIKE :searchTerm'.$i);
                    $splitOrX->add('_.extension LIKE :searchTerm'.$i);
                    $splitOrX->add('_tr.fileName LIKE :searchTerm'.$i);
                    $splitOrX->add('_tr.fileName LIKE :searchTerm'.$i);
                }
                $orX->add($splitOrX);
            }

            $findBy[] = $orX;
        }

        return $findBy;
    }

    protected function browseType(string $type, int $page, $searchTerm = null)
    {
        $repo = $this->getRepo($type);
        if (!$repo instanceof AbstractTranslatableContentRepository) {
            throw new RuntimeException(
                'Repository for media must be tagged with "aa_content.repository" '.
                'and extend "'.AbstractTranslatableContentRepository::class.'"'
            );
        }

        $findBy = $this->prepareBrowseCriteria($type, $page, $searchTerm);

        $all = $repo->contentFindBy(
            $findBy,
            ['_.createdAt' => 'DESC'],
            self::PER_PAGE,
            ($page - 1) * self::PER_PAGE
        );

        return $all;
    }

    public function browseTypeCount(string $type, int $page, $searchTerm = null)
    {
        $repo = $this->getRepo($type);

        $findBy = $this->prepareBrowseCriteria($type, $page, $searchTerm);

        $all = $repo->contentCount($findBy);

        return $all;
    }

    protected function getRepo(string $typeSlug)
    {
        $class = $this->typeResolver->getTypeClass($typeSlug);
        $repo = $this->em->getRepository($class);

        return $repo;
    }

    protected function orderMixedResults(array $results)
    {
        // mix typed result arrays into one, ordered on last update date if present
        usort($results, function ($a, $b) {
            if ($a instanceof UpdatedAtInterface && $a->getUpdatedAt()) {
                $aDate = $a->getUpdatedAt();
            } elseif ($a instanceof CreatedAtInterface) {
                $aDate = $a->getCreatedAt();
            }

            if ($b instanceof UpdatedAtInterface && $b->getUpdatedAt()) {
                $bDate = $b->getUpdatedAt();
            } elseif ($b instanceof CreatedAtInterface) {
                $bDate = $b->getCreatedAt();
            }

            return $aDate <=> $bDate;
        });

        $results = array_reverse($results);

        return $results;
    }

    protected function addInfos(array $results): array
    {
        foreach ($results as $i => $result) {

            $results[$i] = new LibraryItem(
                $result,
                $this->typeResolver->getTypeSlug(get_class($result)),
                $this->urlGenerator->generateWebPath($result),
                $this->sizeConverter->bytesToReadable($result->getSize() ?: 0),
            );
        }

        return $results;
    }
}
