<?php

declare(strict_types=1);

namespace App\Controller\Api;

use App\Entity;
use App\Http\Response;
use App\Http\RouterInterface;
use App\Http\ServerRequest;
use App\OpenApi;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NoResultException;
use OpenApi\Attributes as OA;
use Psr\Http\Message\ResponseInterface;
use Psr\SimpleCache\CacheInterface;

#[
    OA\Get(
        path: '/nowplaying',
        description: "Returns a full summary of all stations' current state.",
        tags: ['Now Playing'],
        parameters: [],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Success',
                content: new OA\JsonContent(
                    type: 'array',
                    items: new OA\Items(ref: '#/components/schemas/Api_NowPlaying')
                )
            ),
        ]
    ),
    OA\Get(
        path: '/nowplaying/{station_id}',
        description: "Returns a full summary of the specified station's current state.",
        tags: ['Now Playing'],
        parameters: [
            new OA\Parameter(ref: OpenApi::REF_STATION_ID_REQUIRED),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Success',
                content: new OA\JsonContent(ref: '#/components/schemas/Api_NowPlaying')
            ),
            new OA\Response(ref: OpenApi::REF_RESPONSE_NOT_FOUND, response: 404),
        ]
    )
]
final class NowPlayingController
{
    public function __construct(
        private readonly EntityManagerInterface $em,
        private readonly CacheInterface $cache
    ) {
    }

    public function getAction(
        ServerRequest $request,
        Response $response,
        ?string $station_id = null
    ): ResponseInterface {
        $router = $request->getRouter();

        if (!empty($station_id)) {
            $np = $this->getForStation($station_id, $router);

            if (null !== $np) {
                return $response->withJson($np);
            }

            return $response->withStatus(404)
                ->withJson(Entity\Api\Error::notFound());
        }

        return $response->withJson(
            $this->getForAllStations(
                $router,
                $request->getAttribute('user') === null // If unauthenticated, hide non-public stations from full view.
            )
        );
    }

    public function getArtAction(
        ServerRequest $request,
        Response $response,
        string $station_id,
        ?string $timestamp = null
    ): ResponseInterface {
        $np = $this->getForStation($station_id, $request->getRouter());

        $currentArt = $np?->now_playing?->song?->art;
        if (null !== $currentArt) {
            return $response->withRedirect((string)$currentArt, 302);
        }

        return $response->withStatus(404)
            ->withJson(Entity\Api\Error::notFound());
    }

    private function getForStation(
        string $station,
        RouterInterface $router
    ): ?Entity\Api\NowPlaying\NowPlaying {
        // Check cache first.
        $np = $this->cache->get('nowplaying.' . $station);

        if (!($np instanceof Entity\Api\NowPlaying\NowPlaying)) {
            // Pull from DB if possible.
            if (is_numeric($station)) {
                $dql = <<<'DQL'
                    SELECT s.nowplaying FROM App\Entity\Station s
                    WHERE s.id = :id
                DQL;
            } else {
                $dql = <<<'DQL'
                    SELECT s.nowplaying FROM App\Entity\Station s
                    WHERE s.short_name = :id
                DQL;
            }

            try {
                $npResult = $this->em->createQuery($dql)
                    ->setParameter('id', $station)
                    ->setMaxResults(1)
                    ->getSingleResult();

                $np = $npResult['nowplaying'] ?? null;
            } catch (NoResultException) {
                return null;
            }
        }

        if ($np instanceof Entity\Api\NowPlaying\NowPlaying) {
            $np->resolveUrls($router->getBaseUrl());
            $np->update();
            return $np;
        }

        return null;
    }

    private function getForAllStations(
        RouterInterface $router,
        bool $publicOnly = false,
    ): array {
        if ($publicOnly) {
            $dql = <<<'DQL'
                SELECT s.nowplaying FROM App\Entity\Station s 
                WHERE s.is_enabled = 1 AND s.enable_public_page = 1
            DQL;
        } else {
            $dql = <<<'DQL'
                SELECT s.nowplaying FROM App\Entity\Station s 
                WHERE s.is_enabled = 1
            DQL;
        }

        $np = [];
        $baseUrl = $router->getBaseUrl();

        foreach ($this->em->createQuery($dql)->getArrayResult() as $row) {
            $npRow = $row['nowplaying'];
            if ($npRow instanceof Entity\Api\NowPlaying\NowPlaying) {
                $npRow->resolveUrls($baseUrl);
                $npRow->update();
                $np[] = $npRow;
            }
        }

        return $np;
    }
}
