<?php

namespace App\Controller;

use App\Service\SpotifyApiService;
use App\Service\SpotifyPlaylistService;
use SpotifyWebAPI\SpotifyWebAPI;
use SpotifyWebAPI\SpotifyWebAPIException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class PlaylistController extends AbstractController
{
    private SpotifyWebAPI $spotifyApi;
    private SpotifyPlaylistService $spotifyPlaylistService;

    public function __construct(SpotifyApiService $spotifyApiService, SpotifyPlaylistService $spotifyPlaylistService)
    {
        // increase timeout limit
        set_time_limit(60);

        $this->spotifyApi = $spotifyApiService->createApi();
        $this->spotifyPlaylistService = $spotifyPlaylistService;
    }

    /**
     * @Route("/playlist-generator", name="playlist-generator")
     */
    public function playlistGeneratorIndex(): Response
    {
        try {
            $me = $this->spotifyApi->me();
        } catch (SpotifyWebAPIException $e) {
            return $this->render('error.html.twig');
        }

        return $this->render('playlist-generator/index.html.twig', [
            'user' => $me,
        ]);
    }

    /**
     * @Route("/playlists/user", name="get-user-playlists")
     */
    public function getPlaylists(): JsonResponse
    {
        $me = $this->spotifyApi->me();

        $rawPlaylists = $this->spotifyApi->getUserPlaylists(
            $me->id,
            [
                'limit' => SpotifyPlaylistService::PLAYLISTS_COUNT,
            ]
        )->items;

        $playlists = [];
        foreach ($rawPlaylists as $playlist) {
            $playlists[] = [
                'id' => $playlist->id,
                'name' => $playlist->name,
                'imageUrl' => $playlist->images[\count($playlist->images) - 1]->url,
            ];
        }

        return new JsonResponse($playlists);
    }

    /**
     * @Route("/show-playlist-content/{playlistId}", name="show-playlist-content")
     */
    public function showPlaylistContent(string $playlistId): Response
    {
        $this->spotifyPlaylistService->printLog('Get content of playlist');
        $playlist = $this->spotifyApi->getPlaylist($playlistId);

        return $this->render('playlist-generator/view.html.twig', [
            'user' => $this->spotifyApi->me(),
            'infos' => $this->spotifyPlaylistService->getPlaylistInformationMessage($playlist),
            'playlist' => $this->spotifyPlaylistService->createDisplayablePlaylist($playlist),
        ]);
    }

    /**
     * @Route("/generate-playlist-from-playlists", name="generate-playlist-from-playlists")
     */
    public function generatePlaylistFromPlaylists(Request $request): RedirectResponse
    {
        /** @var array */
        $selectedPlaylists = $request->query->get('selected-playlist');

        $this->spotifyPlaylistService->printLog('Will create new playlist with recommendations from playlists: ' . join(', ', $selectedPlaylists) . '".');
        $recommendedTrackIds = [];

        $shouldFilterByArtists = (null !== $request->query->get('check-playlist-filter-artists'));
        $this->spotifyPlaylistService->printLog('Filter by artists in source playlists: ' . ($shouldFilterByArtists ? 'yes' : 'no'));

        $generateLongPlaylist = (null !== $request->query->get('check-playlist-long-playlist'));
        $this->spotifyPlaylistService->printLog('Generate long playlist: ' . ($generateLongPlaylist ? 'yes' : 'no'));

        $artistsInSourcePlaylists = [];

        foreach ($selectedPlaylists as $selectedPlaylistId) {
            $playlist = $this->spotifyApi->getPlaylist($selectedPlaylistId);
            $this->spotifyPlaylistService->printLog('Will get recommendations from tracks in playlist "' . $playlist->name . '".');
            $playlistTracks = $playlist->tracks->items;
            $this->spotifyPlaylistService->printLog('Found ' . count($playlistTracks) . ' tracks.');
            foreach ($playlistTracks as $track) {
                $artists = [];
                foreach ($track->track->artists as $artist) {
                    $artists[] = $artist->name;
                    $artistsInSourcePlaylists[$artist->id] = 1;
                }
                $this->spotifyPlaylistService->printLog('Track "' . $track->track->name . '" (' . join(', ', $artists) . ')');

                $recommendations = $this->spotifyApi->getRecommendations([
                    'seed_tracks' => [$track->track->id],
                    'limit' => ($generateLongPlaylist ? 50 : 30),
                ]);
                $this->spotifyPlaylistService->printLog(' -> Got ' . count($recommendations->tracks) . ' recommendatations.');
                foreach ($recommendations->tracks as $recommendedTrack) {
                    $recommendedTrackIds[$recommendedTrack->id] = $recommendedTrack;
                }
            }
        }

        if ($shouldFilterByArtists) {
            $artistsInSourcePlaylists = array_keys($artistsInSourcePlaylists);
            $this->spotifyPlaylistService->printLog('Filter by artists found in source playlists (' . count($artistsInSourcePlaylists) . ')');

            $filteredTrackIds = [];
            foreach ($recommendedTrackIds as $track) {
                foreach ($track->artists as $trackArtist) {
                    if (\in_array($trackArtist->id, $artistsInSourcePlaylists)) {
                        $filteredTrackIds[$track->id] = 1;
                    }
                }
            }
            $recommendedTrackIds = $filteredTrackIds;
        }

        $newPlaylist = $this->spotifyPlaylistService->createPlaylistWithRandomTracks(
            $this->spotifyApi,
            $recommendedTrackIds,
            $generateLongPlaylist ? SpotifyPlaylistService::TRACKS_COUNT_IN_LONG_PLAYLIST : SpotifyPlaylistService::TRACKS_COUNT_IN_SHORT_PLAYLIST,
        );

        $this->addFlash('infos', $this->spotifyPlaylistService->getCreatedPlaylistInformationMessage($newPlaylist));

        return new RedirectResponse($this->generateUrl('show-playlist-content', ['playlistId' => $newPlaylist->id]));
    }

    /**
     * @Route("/generate-playlist-from-top-artists", name="generate-playlist-from-top-artists")
     */
    public function generatePlaylistFromTopArtists(Request $request): RedirectResponse
    {
        // Swith "random" or "selected"
        if (null !== $request->query->get('generate-playlist-random-top-artists')) {
            return $this->generatePlaylistFromRandomTopArtists($request);
        } elseif (null !== $request->query->get('generate-playlist-top-artists')) {
            return $this->generatePlaylistFromSelectedTopArtists($request);
        }

        return new RedirectResponse($this->generateUrl('playlist-generator'));
    }

    private function generatePlaylistFromSelectedTopArtists(Request $request): RedirectResponse
    {
        /** @var array */
        $selectedArtists = $request->query->get('selected-artist');

        $this->spotifyPlaylistService->printLog('Will create new playlist with filterted recommendations from artists: ' . join(', ', $selectedArtists) . '".');
        $recommendedTrackIds = [];

        $shouldFilterByArtists = (null !== $request->query->get('check-top-artist-filter-artists'));
        $this->spotifyPlaylistService->printLog('Filter by artists in selection: ' . ($shouldFilterByArtists ? 'yes' : 'no'));

        $generateLongPlaylist = (null !== $request->query->get('check-top-artist-long-playlist'));
        $this->spotifyPlaylistService->printLog('Generate long playlist: ' . ($generateLongPlaylist ? 'yes' : 'no'));

        foreach ($selectedArtists as $selectedArtist) {
            $this->spotifyPlaylistService->printLog('Get recommendations for artist ' . $selectedArtist . '');
            $recommendations = $this->spotifyApi->getRecommendations([
                'seed_artists' => $selectedArtist,
                'limit' => 100
            ]);
            $this->spotifyPlaylistService->printLog(' -> Got ' . count($recommendations->tracks) . ' recommendatations.');

            // Filter by artist if requested, remove duplicates
            foreach ($recommendations->tracks as $recommendedTrack) {
                if ($shouldFilterByArtists) {
                    foreach ($recommendedTrack->artists as $trackArtist) {
                        if (\in_array($trackArtist->id, $selectedArtists)) {
                            $recommendedTrackIds[$recommendedTrack->id] = 1;
                        }
                    }
                } else {
                    $recommendedTrackIds[$recommendedTrack->id] = 1;
                }
            }
        }

        $newPlaylist = $this->spotifyPlaylistService->createPlaylistWithRandomTracks(
            $this->spotifyApi,
            $recommendedTrackIds,
            $generateLongPlaylist ? SpotifyPlaylistService::TRACKS_COUNT_IN_LONG_PLAYLIST : SpotifyPlaylistService::TRACKS_COUNT_IN_SHORT_PLAYLIST
        );

        $this->addFlash('infos', $this->spotifyPlaylistService->getCreatedPlaylistInformationMessage($newPlaylist));

        return new RedirectResponse($this->generateUrl('show-playlist-content', ['playlistId' => $newPlaylist->id]));
    }

    private function generatePlaylistFromRandomTopArtists(Request $request): RedirectResponse
    {
        $countInTopArtists = random_int(4, 6);
        $countInLessTopArtists = random_int(4, 6);
        $limitBetweenTopAndLessTop = SpotifyPlaylistService::TOP_ARTISTS_COUNT;
        $selectedArtists = $this->spotifyPlaylistService->getRandomArtistsFromTopArtists(
            $this->spotifyApi,
            $countInTopArtists,
            $countInLessTopArtists,
            $limitBetweenTopAndLessTop
        );
        $this->spotifyPlaylistService->printLog('Will create new playlist with filterted recommendations from random artists: ' . join(', ', $selectedArtists) . '".');
        $recommendedTrackIds = [];

        $shouldFilterByArtists = (null !== $request->query->get('check-top-artist-filter-artists'));
        $this->spotifyPlaylistService->printLog('Filter by artists in selection: ' . ($shouldFilterByArtists ? 'yes' : 'no'));

        $generateLongPlaylist = (null !== $request->query->get('check-top-artist-long-playlist'));
        $this->spotifyPlaylistService->printLog('Generate long playlist: ' . ($generateLongPlaylist ? 'yes' : 'no'));

        foreach ($selectedArtists as $selectedArtist) {
            $this->spotifyPlaylistService->printLog('Get recommendations for artist ' . $selectedArtist . '');
            $recommendations = $this->spotifyApi->getRecommendations([
                'seed_artists' => $selectedArtist,
                'limit' => 100
            ]);
            $this->spotifyPlaylistService->printLog(' -> Got ' . count($recommendations->tracks) . ' recommendations.');

            // Filter by artist if requested, remove duplicates
            foreach ($recommendations->tracks as $recommendedTrack) {
                if ($shouldFilterByArtists) {
                    foreach ($recommendedTrack->artists as $trackArtist) {
                        if (\in_array($trackArtist->id, $selectedArtists)) {
                            $recommendedTrackIds[$recommendedTrack->id] = 1;
                        }
                    }
                } else {
                    $recommendedTrackIds[$recommendedTrack->id] = 1;
                }
            }
        }

        $newPlaylist = $this->spotifyPlaylistService->createPlaylistWithRandomTracks(
            $this->spotifyApi,
            $recommendedTrackIds,
            $generateLongPlaylist ? SpotifyPlaylistService::TRACKS_COUNT_IN_LONG_PLAYLIST : SpotifyPlaylistService::TRACKS_COUNT_IN_SHORT_PLAYLIST
        );

        $this->addFlash('infos', $this->spotifyPlaylistService->getCreatedPlaylistInformationMessage($newPlaylist));

        return new RedirectResponse($this->generateUrl('show-playlist-content', ['playlistId' => $newPlaylist->id]));
    }

    /**
     * @Route("/generate-quick-playlist", name="generate-quick-playlist")
     */
    public function generateQuickPlaylist(Request $request): RedirectResponse
    {
        // Swith between "daily mixes" or "tambouille"
        if (null !== $request->query->get('generate-quick-playlist-from-daily-mixes')) {
            return $this->generatePlaylistFromDailyMixes($request);
        } elseif (null !== $request->query->get('generate-quick-playlist-tambouille-mix')) {
            return $this->generateplaylistTambouilleMix($request);
        }

        return new RedirectResponse($this->generateUrl('playlist-generator'));
    }

    private function generatePlaylistFromDailyMixes(Request $request): RedirectResponse
    {
        $generateLongPlaylist = (null !== $request->query->get('check-quick-create-long-playlist'));

        $this->spotifyPlaylistService->printLog('Generate playlist from Daily Mixes (long playlist :' . ($generateLongPlaylist ? 'yes' : 'no') . ')');

        $newPlaylist = $this->spotifyPlaylistService->createPlaylistFromDailyMixesTracks(
            $this->spotifyApi,
            $generateLongPlaylist ? SpotifyPlaylistService::TRACKS_COUNT_IN_LONG_PLAYLIST : SpotifyPlaylistService::TRACKS_COUNT_IN_SHORT_PLAYLIST
        );

        $this->addFlash('infos', $this->spotifyPlaylistService->getCreatedPlaylistInformationMessage($newPlaylist));

        return new RedirectResponse($this->generateUrl('show-playlist-content', ['playlistId' => $newPlaylist->id]));
    }

    private function generateplaylistTambouilleMix(Request $request): RedirectResponse
    {
        $generateLongPlaylist = (null !== $request->query->get('check-quick-create-long-playlist'));

        $this->spotifyPlaylistService->printLog('Generate tambouille mix playlist (long playlist :' . ($generateLongPlaylist ? 'yes' : 'no') . ')');

        $newPlaylist = $this->spotifyPlaylistService->createPlaylistTambouilleMix(
            $this->spotifyApi,
            $generateLongPlaylist ? SpotifyPlaylistService::TRACKS_COUNT_IN_LONG_PLAYLIST : SpotifyPlaylistService::TRACKS_COUNT_IN_SHORT_PLAYLIST
        );

        $this->addFlash('infos', $this->spotifyPlaylistService->getCreatedPlaylistInformationMessage($newPlaylist));

        return new RedirectResponse($this->generateUrl('show-playlist-content', ['playlistId' => $newPlaylist->id]));
    }
}