<?php

namespace App\Tmdb\Framework\Serializer;

use Aimeos\Map;
use App\Base\Enum\MediaType;
use App\Tmdb\Dto\CastMemberDto;
use App\Tmdb\Dto\CrewMemberDto;
use App\Tmdb\Dto\GenreDto;
use App\Tmdb\Dto\WatchProviderDto;
use App\Tmdb\TmdbResult;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class TmdbResultDenormalizer implements DenormalizerInterface
{
    const POSTER_IMG_PATH = "https://image.tmdb.org/t/p/w500";

    public function __construct(
        #[Autowire(service: 'serializer.normalizer.object')]
        private readonly NormalizerInterface $normalizer,
    ) {}

    public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): TmdbResult|array|null
    {
        $result = $this->normalizer->denormalize($data, TmdbResult::class, $format, $context);
        $result->stars = $this->getStars($data);
        $result->directors = $this->getDirectors($data);
        $result->producers = $this->getProducers($data);
        $result->creators = $this->getCreators($data);
        $result->watchProviders = $this->getWatchProviders($data);
        return $result;
    }

    protected function getStars(array $data): ?array
    {
        if (!array_key_exists('credits', $data)) {
            return null;
        }
        return Map::from($data['credits']['cast'])
            ->slice(0, 3)
            ->map(fn($item) => $this->normalizer->denormalize($item, CastMemberDto::class))
            ->toArray();
    }

    protected function getDirectors(array $data): ?array
    {
        if (!array_key_exists('credits', $data)) {
            return null;
        }
        return Map::from($data['credits']['crew'])
            ->filter(fn($item) => $item['job'] === 'Director')
            ->slice(0, 3)
            ->map(fn($item) => $this->normalizer->denormalize($item, CrewMemberDto::class))
            ->toArray();
    }

    public function getCreators(array $data): ?array
    {
        if (!array_key_exists('credits', $data)) {
            return null;
        }
        return Map::from($data['credits']['crew'])
            ->filter(fn($item) => $item['job'] === 'Creator')
            ->map(fn($item) => $this->normalizer->denormalize($item, CrewMemberDto::class))
            ->toArray();
    }

    public function getProducers(array $data): ?array
    {
        if (!array_key_exists('credits', $data)) {
            return null;
        }
        return Map::from($data['credits']['crew'])
            ->filter(fn($item) => $item['job'] === 'Producer')
            ->map(fn($item) => $this->normalizer->denormalize($item, CrewMemberDto::class))
            ->toArray();
    }

    public function getGenres(array $data, MediaType $mediaType): ?array
    {
        if (array_key_exists('genres', $data)) {
            return null;
        }

        return Map::from($data['genres'])
            ->map(fn($item) => $this->normalizer->denormalize($item, GenreDto::class))
            ->toArray();
    }

    public function getWatchProviders(array $data): ?array
    {
        if (!array_key_exists('watch/providers', $data)) {
            return null;
        }
        // ToDo: Make region configurable
        return Map::from($data['watch/providers']['results']['US']['flatrate'])
            ->map(fn($item) => $this->normalizer->denormalize($item, WatchProviderDto::class))
            ->toArray();
    }

    public function supportsDenormalization(
        mixed $data,
        string $type,
        ?string $format = null,
        array $context = []
    ): bool {
        return !array_key_exists('media_type', $context);
    }

    public function getSupportedTypes(?string $format): array
    {
        return [
            TmdbResult::class => false,
        ];
    }
}
