<?php

namespace App\Http\Controllers\Api\Platform;

use App\Http\Controllers\Controller;
use App\Models\Book;
use App\Models\Community;
use App\Models\Movie;
use App\Models\Music;
use App\Models\MusicAlbum;
use App\Models\PostAttachment;
use App\Models\ProductAttachment;
use App\Models\RealtimeMessageAttachment;
use App\Models\Series;
use App\Models\SeriesSeason;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class StorageFileController extends Controller
{
   const thumbnailFlag = 'thumbnail';
   const userAvatarFlag = 'avatar';
   const communityFlag = 'community';
   const productAttachment = 'pa';
   const ratingAttachment = 'ra';
   const postAttachment = 'poa';
   const realtimeMessageAttachment = 'rtma';
   const bookCover = 'bc';
   const musicCover = 'muc';
   const musicAlbumCover = 'mac';
   const seriesCover = 'sc';
   const movieCover = 'mc';
   const seriesSeasonCover = 'ssc';
   const movieFile = 'mf';
   const episodeFile = 'ef';
   const musicFile = 'muf';
   const bookFile = 'bf';

   /**
    * Show the application dashboard.
    *
    * @param Request $request
    * @return BinaryFileResponse
    */
   public function serveDocFile(Request $request): BinaryFileResponse
   {
      $fileName = $request->get('file');
      $resourceId = $request->get('resourceId');
      $resource = null;
      $prefix = null;

      if ($request->get('q') === self::userAvatarFlag) {
         /** @var $user $resource */
         $resource = User::findById($resourceId);
         $prefix = User::$prefixDir;
      }

      if ($request->get('q') === self::communityFlag) {
         /** @var Community $resource */
         $resource = Community::findById($resourceId);

         if ($resource !== null) {
            // Build path
            $path = $resource->buildCommunityFilesPath($fileName);

            $binaryFile = $this->serve($path);
            if ($binaryFile !== null) return $binaryFile;
         }
      }

      if ($request->get('q') === self::productAttachment) {
         /** @var ProductAttachment $productAttachment */
         $productAttachment = ProductAttachment::findById($resourceId);

         if ($productAttachment !== null) {
            // Build path
            $path = $productAttachment->buildAttachmentFilesPath($fileName);

            $binaryFile = $this->serve($path);
            if ($binaryFile !== null) return $binaryFile;
         }
      }

      if ($request->get('q') === self::bookCover) {
         /** @var Book $resource */
         $resource = Book::findById($resourceId);

         if ($resource !== null) {
            // Build path
            $path = $resource->buildBookFilesPath($fileName);

            $binaryFile = $this->serve($path);
            if ($binaryFile !== null) return $binaryFile;
         }
      }

      if ($request->get('q') === self::musicCover) {
         /** @var Music $resource */
         $resource = Music::findById($resourceId);

         if ($resource !== null) {
            // Build path
            $path = $resource->buildMusicFilesPath($fileName);

            $binaryFile = $this->serve($path);
            if ($binaryFile !== null) return $binaryFile;
         }
      }

      if ($request->get('q') === self::musicAlbumCover) {
         /** @var MusicAlbum $resource */
         $resource = MusicAlbum::findById($resourceId);

         if ($resource !== null) {
            // Build path
            $path = $resource->buildAlbumFilesPath($fileName);

            $binaryFile = $this->serve($path);
            if ($binaryFile !== null) return $binaryFile;
         }
      }

      if ($request->get('q') === self::seriesCover) {
         $binaryFile = $this->handleSeriesCover($resourceId, $fileName);

         if ($binaryFile !== null) return $binaryFile;
      }

      if ($request->get('q') === self::movieCover) {
         /** @var Movie $resource */
         $resource = Movie::findById($resourceId);

         if ($resource !== null) {
            // Build path
            $path = $resource->buildMovieFilesPath($fileName);

            $binaryFile = $this->serve($path);
            if ($binaryFile !== null) return $binaryFile;
         }
      }

      if ($request->get('q') === self::seriesSeasonCover) {
         $binaryFile = self::handleSeriesSeasonCover((int)$resourceId, $fileName);
         if ($binaryFile !== null) return $binaryFile;
      }

      if ($request->get('q') === self::postAttachment) {
         /** @var PostAttachment $postAttachment */
         $postAttachment = PostAttachment::findById($resourceId);

         if ($postAttachment !== null) {
            // Build path
            $path = $postAttachment->buildAttachmentFilesPath($fileName);

            $binaryFile = $this->serve($path);
            if ($binaryFile !== null) return $binaryFile;
         }
      }

      if ($request->get('q') === self::realtimeMessageAttachment) {
         /** @var RealtimeMessageAttachment $realtimeMessageAttachment */
         $realtimeMessageAttachment = RealtimeMessageAttachment::findById($resourceId);

         if ($realtimeMessageAttachment !== null) {
            // Build path
            $path = $realtimeMessageAttachment->buildAttachmentFilePath($fileName);

            $binaryFile = $this->serve($path);
            if ($binaryFile !== null) return $binaryFile;
         }
      }

      if ($request->get('q') === self::thumbnailFlag) {
         $resultPath = sprintf(
            config('torryme.paths.thumbnails'),
            $fileName,
         );

         $binaryFile = $this->serve($resultPath);
         if ($binaryFile !== null) return $binaryFile;
      }

      if ($resource !== null && !empty($fileName) && !empty($prefix)) {
         $path = absolute_doc_path($resource, $fileName, $prefix);
         $binaryFile = $this->serve($path);

         if ($binaryFile !== null) return $binaryFile;
      }

      abort(404);
   }

   // Utilities

   /**
    * Serve server file
    *
    * @param string $path
    * @return BinaryFileResponse|null
    */
   public function serve(string $path): BinaryFileResponse|null
   {
      $storageOption = config('torryme.storage_option');

      if (Storage::disk($storageOption)->exists($path)) {
         if ($storageOption === 'local') {
            $path = storage_path('app/' . $path);
            if ($path !== null) {
               return response()->file($path);
            }
         }
      }

      return null;
   }

   /**
    * Handle series cover file
    *
    * @param int $resourceId
    * @param string $fileName
    * @return BinaryFileResponse|null
    */
   public function handleSeriesCover(int $resourceId, string $fileName): ?BinaryFileResponse
   {
      /** @var Series $resource */
      $resource = Series::findById($resourceId);

      if ($resource !== null) {
         // Build path
         $path = $resource->buildSeriesFilePath($fileName);

         return $this->serve($path);
      }

      return null;
   }

   /**
    * Handle series season cover file
    *
    * @param int $resourceId
    * @param string $fileName
    * @return BinaryFileResponse|null
    */
   public function handleSeriesSeasonCover(int $resourceId, string $fileName): ?BinaryFileResponse
   {
      $series = null;

      try {
         /** @var SeriesSeason $resource */
         $resource = SeriesSeason::findById($resourceId);

         if ($resource !== null) {
            $series = $resource->{'series'};
            $seasonEpisodes = $resource->{'seasonEpisodes'};

            if (count($seasonEpisodes) > 0) {
               if (count($seasonEpisodes) === 1) {
                  $randomEpisode = $seasonEpisodes->first();
               } else {
                  $randomEpisode = $seasonEpisodes->slice(rand(0, count($seasonEpisodes) - 1), 1);
                  $randomEpisode = $randomEpisode->first();
               }

               $path = $randomEpisode->buildEpisodeFilePath($randomEpisode->{'file_path'});
               $resultPath = extract_video_thumbnail($path, $fileName);
               if (filled($resultPath)) {
                  return $this->serve($resultPath);
               }
            }

            return $this->handleSeriesCover($series->{'id'}, $series->{'cover'});
         }
      } catch (\Exception $exception) {
         log_debug($exception, "StorageFileController::handleSeriesSeasonCover");
      }

      if ($series !== null) {
         return $this->handleSeriesCover($series->{'id'}, $series->{'cover'});
      }

      return null;
   }
}
