<?php

namespace App\Services;

use App\Exceptions\SubmissionException;
use App\Helpers\EntryHelpers;
use App\Helpers\PlayOnlineHelpers;
use App\Helpers\XenForoHelpers;
use App\Http\Requests\StoreEntryRequest;
use App\Jobs\CreateXenForoCommentsThread;
use App\Jobs\DeleteFile;
use App\Models\Author;
use App\Models\Category;
use App\Models\Entry;
use App\Models\EntryFile;
use App\Models\Gallery;
use App\Models\EntryHash;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Language;
use App\Models\Modification;
use App\Models\Platform;
use App\Models\System;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;

/**
 * @phpstan-import-type FSFileData from \App\Types\FSTypes
 */
class SubmissionsService {

    /**
     * Request for store/edit.
     * @var Request|null
     */
    private ?Request $request = null;

    /**
     * Section for store/edit.
     * @var string|null
     */
    private ?string $section = null;

    /**
     * Entry for edit.
     * @var Entry|null
     */
    private ?Entry $entry = null;

    /**
     * @return list<FSFileData>
     */
    public function prepareOldFiles( ?Entry $entry = null ): array
    {
        if( $entry === null ){
            $files = old( 'files_uuid', [] );
        } else {
            $files = old( 'files_uuid', $entry->files->pluck('file_uuid')->toArray() );
        }

        if( $files === [] )
            return [];

        $service = app(FileServersService::class);

        return array_map(
            function( string $uuid ) use ($service) {
                $file = EntryFile::where('file_uuid', $uuid)->first();

                if( $file )
                    return [
                        'name' => $file->filename,
                        'totalChunks' => 0, // Already uploaded.
                        'rawFile' => null,
                        'progressValue' => 0,
                        'currentChunk' => 0,
                        'done' => true,
                        'error' => null,
                        'uuid' => $uuid,
                        'state' => $file->state,
                        'file_explorer' => $service->getArchiveExplorerUrl($file),
                        'file_explorer_files' => null,
                        'download_url' => $service->getDownloadFileUrl($file, false),
                        'can_be_online_patched' => EntryHelpers::enableOnlinePatcherBasedOnExtension($file['filename']),
                        'meta_online_patcher' => $file->online_patcher,
                        'meta_secondary_online_patcher' => $file->secondary_online_patcher,
                        'meta_play_online' => $file->playOnlineSetting()->exists() ? true : false,
                        'meta_play_online_core' => $file->playOnlineSetting()->exists() ? $file->playOnlineSetting->core : '',
                        'meta_play_online_threads' => $file->playOnlineSetting()->exists() ? $file->playOnlineSetting->threads : false,
                    ];

                $file = Cache::get("uploaded_file_{$uuid}");
                if( $file )
                    return [
                        'name' => $file['filename'],
                        'totalChunks' => 0, // Already uploaded.
                        'rawFile' => null,
                        'progressValue' => 0,
                        'currentChunk' => 0,
                        'done' => true,
                        'error' => null,
                        'uuid' => $uuid,
                        'state' => $file['state'],
                        'file_explorer' => null,
                        'file_explorer_files' => null,
                        'download_url' => null,
                        'can_be_online_patched' => EntryHelpers::enableOnlinePatcherBasedOnExtension($file['filename']),
                        'meta_online_patcher' => false,
                        'meta_secondary_online_patcher' => false,
                        'meta_play_online' => false,
                        'meta_play_online_core' => null,
                        'meta_play_online_threads' => false
                    ];

                return null;
             },
        $files );
    }

    /**
     * @param StoreEntryRequest $request
     * @param string $section
     *
     * @return Entry
     * @throws SubmissionException
     * @throws \Throwable
     */
    public function storeEntry( Request $request, string $section ){

        // STEP 1 : Prepare basic fields.

        $this->request = $request;
        $this->section = $section;
        $user_id = \Auth::user()->user_id;

        $entry = DB::transaction(function () use ( $user_id ) {

            // STEP 2 : Create game.
            $gameId = $this->Step2_CreateAndReturnGameId();

            // STEP 3 : Create Complete title.
            $completeTitle = $this->Step3_BuildCompleteTitle( $gameId );

            // STEP 4 : Generate slug and entry title.
            $entrySlug = EntryHelpers::uniqueSlug( $completeTitle, Entry::class );

            if( section_must_be( 'translations', $this->section ) &&
                !$this->request->input('entry_title') ){
                $entryTitle = Game::find($gameId)->name ?? "";
            } else {
                $entryTitle = $this->request->input('entry_title');
            }

            // STEP 5 : Removed / Delayed.
            // $mainImage = $this->Step5_MoveMainImage();

            // STEP 6 : Prepare entry fields and save entry.
            $fields = [
                'type' => $this->section,
                'title' => $entryTitle,
                'slug' => $entrySlug,
                'description' => $this->request->input('description'),
                'main_image' => $this->request->input('main-image'),
                'state' => $this->request->input('submit-state'),
                'game_id' => $gameId,
                'platform_id' => $this->request->input('platform_only_id'),
                'status_id' => $this->request->input('status'),
                'version' => $this->request->input('version'),
                'release_date' => $this->request->input('release-date'),
                'staff_credits' => $this->request->input('staff_credits'),
                'relevant_link' => $this->request->input('release_site'),
                'youtube_link' => $this->request->input('youtube_video'),
                'user_id' => $user_id,
                'complete_title' => $completeTitle,
                'level_id' => $this->request->input('level'),
                'nsfw' => $this->request->input('nsfw-entry') ?? false,
            ];

            $entry = Entry::create( $fields );

            // STEP 7 : Save entry fields.
            $this->Step7_SaveEntryFiles( $entry );

            // STEP 8 : Save hashes.
            if( section_must_be( ['translations', 'romhacks' ], $this->section ) )
                $this->Step8_SaveHashes( $entry->id );

            // STEP 9 : Save Authors.
            $this->Step9_SaveAuthors( $entry );

            // STEP 10 : Save Modifications.
            if( section_must_be( ['romhacks','lua-scripts'], $this->section ) ){
                $this->Step10_SaveRomhacksModifications( $entry );
            }
            if( section_must_be( 'utilities', $this->section ) ){
                $this->Step10_SaveUtilitiesSystems( $entry );
            }

            // STEP 11 : Save Languages
            $this->Step11_SaveLanguages( $entry );

            // STEP 11.5 : Save Categories
            if( section_must_be( ['utilities', 'documents'], $this->section ) ) {
                $this->Step11_5_SaveCategories($entry);
            }

            // STEP 12 : Prepare Gallery images.
            $this->Step12a_PrepareGalleryImages( $entry );

            return $entry;

        });

        // Step 12, Move main image and gallery.
        $this->Step12b_MoveMainImage( $entry );
        $this->Step12c_SaveGalleryImages( $entry );

        // Step 13: Try to create the comments section.
        $this->Step13_CreateCommentsThread( $entry );

        // Step 14: Refresh XF count.
        if( $entry->state !== 'draft')
            XenForoHelpers::updateEntriesCount( $entry->user_id );

        return $entry;

    }

    /**
     * @return null|int
     *
     * @throws SubmissionException
     */
    private function Step2_CreateAndReturnGameId(): ?int {

        $mode = $this->request->input('game_selection_mode', 'game');
        if( $mode !== 'game' )
            return null;

        // Already existing game.
        if( $this->request->input('game_id') )
            return $this->request->input('game_id');

        // No fields like a draft.
        if( !$this->request->input('new-game-title') &&
            !$this->request->input('new-game-platform') &&
            !$this->request->input('new-game-genre') )
            return null;

        // Need to create a game.
        $game = $this->createGameFromFormFields();

        return $game->id;
    }

    private function createGameFromFormFields(): Game
    {
        if( !$this->request->input('new-game-title') || !$this->request->input('new-game-platform') || !$this->request->input('new-game-genre') )
            throw new SubmissionException( "New game informations is missing" );

        $platform = Platform::find( $this->request->input('new-game-platform') );
        $genre = Genre::find( $this->request->input('new-game-genre') );

        if( !$platform || !$genre )
            throw new SubmissionException( "Incorrect game platform id" );

        $gameSlug = EntryHelpers::uniqueSlug( $this->request->input('new-game-title'), Game::class );

        return Game::create([
            'name' => trim( $this->request->input('new-game-title') ),
            'slug' => $gameSlug,
            'platform_id' => $platform->id,
            'genre_id' => $genre->id,
        ]);
    }

    /**
     * Prepare and build complete title.
     *
     * @param int|null $gameId
     *
     * @return string
     */
    private function Step3_BuildCompleteTitle( ?int $gameId = null ): string {
        $fields = [];

        $fields['entry_title'] = $this->request->input('entry_title') ?? null;
        if( section_must_be( [ 'homebrew', 'translations' ], $this->section ) && $gameId ){
            $fields['game_name'] = $gameId ? Game::find( $gameId )->name : null;
        }
        if( section_must_be( 'translations', $this->section ) ) {
            $fields['languages_string'] = Language::whereIn('id', $this->request->input('languages', []))->pluck('name')->implode(', ');
        }
        if( section_must_be(['romhacks', 'translations', 'homebrew', 'lua-scripts'], $this->section ) ) {
            // TODO: Add single platform ID compatibility.
            $fields['platform_name'] = $gameId ? Game::find( $gameId )->platform->name : Platform::find( $this->request->input('platform_only_id') )?->name ?? null;
        }

        return EntryHelpers::buildCompleteTitle( $this->section, $fields );
    }

    /**
     * @param int $entryId
     *
     * @return void
     * @throws SubmissionException
     */
    private function Step7_SaveEntryFiles( Entry $entry, ?array $uuidData = null ): void
    {
        if( !$uuidData )
            $uuidData = $this->request->input('files_uuid', [] );

        $metadataArray = $this->request->input('files_metadata', []);

        foreach ( $uuidData ?? [] as $uuid ) {
            $fileData = Cache::pull("uploaded_file_{$uuid}");
            if( !$fileData )
                throw new SubmissionException( "File {$uuid} has expired. Please delete all your files and retry. If it's an edition, delete all the new files and retry." );

            if( section_must_be( [ 'romhacks', 'translations' ], $entry->type ) ) {
                $onlinePatcher = (bool)($metadataArray[$uuid]['online_patcher'] ?? false);
                if (!$onlinePatcher)
                    $onlinePatcher = EntryHelpers::enableOnlinePatcherBasedOnExtension($fileData['filename']);

                $secondaryOnlinePatcher = (bool)($metadataArray[$uuid]['secondary_online_patcher'] ?? false);
            } else {
                $onlinePatcher = false;
                $secondaryOnlinePatcher = false;
            }

            $file = EntryFile::create([
                'entry_id' => $entry->id,
                'file_uuid' => $uuid,
                'filename' => $fileData['filename'],
                'filepath' => $fileData['filepath'],
                'favorite_server' => $fileData['favorite_server'],
                'favorite_at' => \DateTimeImmutable::createFromTimestamp( $fileData['favorite_at'] ),
                'filesize' => $fileData['filesize'],
                'state' => 'public',
                'online_patcher' => $onlinePatcher,
                'secondary_online_patcher' => $secondaryOnlinePatcher,
            ]);

            if( section_must_be( ['romhacks', 'translations', 'homebrew'], $entry->type ) ) {
                $playOnline = (bool)($metadataArray[$uuid]['play_online'] ?? false);
                $playOnlineCore = $metadataArray[$uuid]['play_online_core'] ?? null;
                $playOnlineThreads = (bool)($metadataArray[$uuid]['play_online_threads'] ?? false);

                if (!$playOnline && $entry->getRealPlatform()?->play_online_core !== null) {
                    $playOnline = true;
                    $playOnlineCore = $entry->getRealPlatform()?->play_online_core;
                }

                if ($playOnline) {
                    $file->playOnlineSetting()->updateOrCreate(
                        ['file_id' => $file->id],
                        [
                            'core' => $playOnlineCore,
                            'threads' => $playOnlineThreads,
                        ]
                    );
                }
            }
        }
    }

    /**
     * @param int $entryId
     *
     * @return void
     */
    private function Step8_SaveHashes( int $entryId ): void
    {
        foreach ( $this->request->input('hashes', [] ) ?? [] as $hash ) {
            if( !isset($hash['filename'], $hash['hash_crc32'], $hash['hash_sha1'], $hash['verified']) ) {
                continue;
            }

            EntryHash::create([
               'entry_id' => $entryId,
               'filename' => $hash['filename'],
               'hash_crc32' => $hash['hash_crc32'],
               'hash_sha1' => $hash['hash_sha1'],
               'verified' => $hash['verified'],
            ]);
        }
    }

    /**
     * @param Entry $entry
     *
     * @return void
     * @throws SubmissionException
     */
    private function Step9_SaveAuthors( Entry $entry ): void
    {
        // TODO: Code fragment to be replaced by edit version.

        // Existing authors.
        foreach ( $this->request->input('authors', [] ) ?? [] as $authorId ) {
            $author = Author::find( $authorId );
            if( !$author )
                throw new SubmissionException( "Author {$authorId} does not exist." );
            $entry->authors()->attach( $author->id );
        }

        // New Authors
        foreach ( $this->request->input('new-authors', [] ) ?? [] as $authorName ) {
            $authorName = trim( $authorName );
            if( $authorName === '' )
                continue;

            $author = Author::firstOrCreate(
               ['slug' => EntryHelpers::uniqueSlug( $authorName, Author::class )],
               ['name' => $authorName]
            );
            $entry->authors()->attach( $author->id );
        }
    }

    /**
     * @param Entry $entry
     *
     * @return void
     * @throws SubmissionException
     */
    private function Step10_SaveRomhacksModifications( Entry $entry ): void
    {
        // TODO: Replace by edit version

        foreach ( $this->request->input('modifications', [] ) ?? [] as $modificationId ) {
            $modification = Modification::find( $modificationId );
            if( !$modification )
                throw new SubmissionException( "Modification {$modificationId} does not exist." );
            $entry->modifications()->attach( $modification->id );
        }
    }

    /**
     * @param Entry $entry
     *
     * @return void
     * @throws SubmissionException
     */
    private function Step10_SaveUtilitiesSystems( Entry $entry ): void
    {
        // TODO: Replace by edit version

        foreach ( $this->request->input('systems', [] ) ?? [] as $systemId ) {
            $system = System::find( $systemId );
            if( !$system )
                throw new SubmissionException( "System {$systemId} does not exist." );
            $entry->systems()->attach( $system->id );
        }
    }

    /**
     * @param Entry $entry
     *
     * @return void
     * @throws SubmissionException
     */
    private function Step11_SaveLanguages( Entry $entry ): void
    {
        // TODO: Replace by edit version.

        foreach ( $this->request->input('languages', [] ) ?? [] as $languageId ) {
            $language = Language::find( $languageId );
            if( !$language )
                throw new SubmissionException( "Language {$languageId} does not exist." );
            $entry->languages()->attach( $language->id );
        }

    }

    /**
     * @param Entry $entry
     *
     * @return void
     * @throws SubmissionException
     */
    private function Step11_5_SaveCategories( Entry $entry ): void
    {
        // TODO: Replace by edit version.

        foreach ( $this->request->input('categories', [] ) ?? [] as $categoryId ) {
            $category = Category::find( $categoryId );
            if( !$category )
                throw new SubmissionException( "Category {$categoryId} does not exist." );
            $entry->categories()->attach( $category->id );
        }

    }

    private function Step12a_PrepareGalleryImages( Entry $entry ): void
    {
        foreach ( $this->request->input('gallery', [] ) ?? [] as $i => $imagePath ) {
            $entry->gallery()->create([
                'image' => $imagePath,
                'order' => $i
            ]);
        }
    }

    /**
     * @param Entry $entry
     *
     * @return void
     */
    private function Step12b_MoveMainImage( Entry $entry ): void {
        $mainImage = $entry->main_image;

        if( !$mainImage )
            return;

        $newPath = 'entries/main-images/' . basename($mainImage);

        if( !Storage::disk('public')->move($mainImage, $newPath) )
            return;

        $entry->update(['main_image' => $newPath]);
    }

    private function Step12c_SaveGalleryImages( Entry $entry ): void
    {
        foreach ( $entry->gallery ?? [] as $galleryItem ) {
            $newPath = 'entries/gallery-images/' . $entry->id . '/' . basename($galleryItem->image);

            if( !Storage::disk('public')->move($galleryItem->image, $newPath) )
                continue;

            $galleryItem->update(['image' => $newPath]);
        }
    }

    public function editEntry(Request $request, string $section, Entry $entry ): Entry
    {

        // STEP 1: Prepare basic fields and keep in save some others fields.
        $this->request = $request;
        $this->section = $section;
        $this->entry = $entry;

        if( \Auth::user()->can('moderate', $entry) ){
            $user_id = $this->request->input('owner_user_id');
            $oldUserId = $this->entry->user_id;
        } else {
            $user_id = \Auth::user()->user_id;
            $oldUserId = null;
        }

        $oldMainImage = $entry->main_image;
        $galleryPaths = [];

        $entry = DB::transaction( function() use ( $user_id, &$galleryPaths ){

            // STEP 2: Create game if different.
            $gameId = null;
            $gameId = $this->eStep2_VerifyCreateAndEditGameId();

            // STEP 3: Recreate complete title and refresh slug if needed.
            $completeTitle = $this->Step3_BuildCompleteTitle( $gameId );
            if( $completeTitle !== $this->entry->complete_title ) {
                $this->entry->complete_title = $completeTitle;
                $this->entry->slug = EntryHelpers::uniqueSlug( $completeTitle, Entry::class, $this->entry->id );
            }

            // STEP 4: Regenerate entry title.

            if( section_must_be( 'translations', $this->section ) &&
                !$this->request->input('entry_title') ){
                $this->entry->title = Game::find($gameId)->name;
            } else {
                $this->entry->title = $this->request->input('entry_title');
            }

            // STEP 5: Update entry fields.

            $fields = [
                'type' => $this->section,
                'title' => $this->entry->title, // Useless, I know.
                'slug' => $this->entry->slug,
                'description' => $this->request->input('description'),
                'main_image' => $this->request->input('main-image'),
                'state' => $this->request->input('submit-state'),
                'game_id' => $gameId,
                'platform_id' => $this->request->input('platform_only_id'),
                'status_id' => $this->request->input('status'),
                'version' => $this->request->input('version'),
                'release_date' => $this->request->input('release-date'),
                'staff_credits' => $this->request->input('staff_credits'),
                'relevant_link' => $this->request->input('release_site'),
                'youtube_link' => $this->request->input('youtube_video'),
                'user_id' => $user_id,
                'complete_title' => $completeTitle,
                'level_id' => $this->request->input('level'),
            ];

            if( \Auth::user()->can('moderate', $this->entry) ){
                $fields['staff_comment'] = $this->request->input('staff_comment');
                $fields['featured'] = $this->request->input('featured') ?? false;
                if( $fields['featured'] == true && $this->entry->featured_at === null )
                    $fields['featured_at'] = now();
                if( $fields['featured'] == false )
                    $fields['featured_at'] = null;
                $fields['comments_thread_id'] = $this->request->input('comments_thread_id');
                $refresh_created_at = $this->request->input('refresh_created_at') ?? false;
                if( $refresh_created_at )
                    $fields['created_at'] = now();
                $fields['nsfw'] = $this->request->input('nsfw-entry') ?? false;
            }

            $this->entry->update( $fields );

            // STEP 6: Update entry files.
            $this->eStep6_UpdateEntryFiles( $this->entry->id );

            // STEP 7: Update hashes.
            if( section_must_be( ['translations', 'romhacks' ], $this->section ) )
                $this->eStep7_UpdateHashes( $this->entry->id );

            // STEP 8: Update Authors.
            $this->eStep8_UpdateAuthors();

            // STEP 9: Update romhacks modifications.
            if( section_must_be( ['romhacks', 'lua-scripts'], $this->section ) ) {
                $this->eStep9_UpdateRomhacksModifications();
            }
            if( section_must_be( 'utilities', $this->section ) ) {
                $this->eStep9_UpdateUtilitiesSystems();
            }

            // STEP 10: Update Languages.
            $this->eStep10_UpdateLanguages();

            // STEP 10.5 : Update categories
            if( section_must_be( ['utilities', 'documents'], $this->section ) )
                $this->eStep10_5_UpdateCategories();

            // STEP 11: Prepare new gallery images and prepare deletion of others ones.
            $galleryPaths = $this->eStep11a_UpdateGalleryImages();

            return $this->entry;

        });

        // STEP 11 : Update main image if needed.
        $this->eStep11b_UpdateMainImage( $oldMainImage );

        // STEP 11 : Update gallery storage.
        $this->eStep11c_UpdateGalleryImages( $galleryPaths );

        // STEP 12: Refresh XF count.
        if( $entry->state !== 'draft' ) {
            if ($oldUserId)
                XenForoHelpers::updateEntriesCount($oldUserId);
            XenForoHelpers::updateEntriesCount($entry->user_id);
        }

        // STEP 13: Try to create comments area if it doesn't exist.
        $this->Step13_CreateCommentsThread( $this->entry );

        return $entry;
    }

    /**
     * @throws SubmissionException
     */
    private function eStep2_VerifyCreateAndEditGameId(): ?int
    {

        $mode = $this->request->input('game_selection_mode', 'game');
        if ($mode !== 'game') {
            return null;
        }

        // Already existing game.
        if( $this->request->input('game_id') ){

            if( $this->entry->game_id == $this->request->input('game_id') ){

                return $this->entry->game_id; // No changes.

            } else { // Change in game but already exist.

                $game = Game::find( $this->request->input('game_id') );
                if( !$game )
                    throw new SubmissionException( "Game {$this->request->input('game_id')} does not exist." );
                $this->entry->game_id = $game->id;
                return $this->entry->game_id;

            }

        }

        // In draft.
        if( !$this->request->input('new-game-title') &&
            !$this->request->input('new-game-platform') &&
            !$this->request->input('new-game-genre') )
            return $this->entry->game_id;

        // Need to create a game.
        $game = $this->createGameFromFormFields();

        $this->entry->game_id = $game->id;
        return $this->entry->game_id;
    }

    /**
     * @throws SubmissionException
     */
    private function eStep6_UpdateEntryFiles(int $entryId ): void
    {
        $requestUuids = $this->request->input('files_uuid', []) ?? [];
        $requestStates = $this->request->input('files_state', []) ?? [];
        $existingUuids = EntryFile::where( 'entry_id', $entryId )->pluck('file_uuid')->toArray();

        $needDeletion = array_diff( $existingUuids, $requestUuids );
        if( !empty( $needDeletion ) ){
            $userId = \Auth::user()->user_id;
            EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->whereNot('state', 'archived')->get()->each( function ( $f ) use ( $userId ) {
                DeleteFile::dispatch( $f->filepath, $f->filename, $userId);
            });
            EntryFile::where('entry_id', $entryId)->whereIn('file_uuid', $needDeletion)->whereNot('state', 'archived')->delete();
        }

        $needAddition = array_diff( $requestUuids, $existingUuids );

        if( !empty( $needAddition ) ){
            $this->Step7_SaveEntryFiles( $this->entry, $needAddition ); // Same code.
        }

        $metadataArray = $this->request->input('files_metadata', []);
        $stateMap = array_combine( $requestUuids, $requestStates );

        foreach( $stateMap as $uuid => $state ){

            if( section_must_be( ['romhacks', 'translations'], $this->entry->type ) ) {
                $onlinePatcher = (bool)($metadataArray[$uuid]['online_patcher'] ?? false);
                $secondaryOnlinePatcher = (bool)($metadataArray[$uuid]['secondary_online_patcher'] ?? false);
            } else {
                $onlinePatcher = false;
                $secondaryOnlinePatcher = false;
            }

            $entryFile = EntryFile::where('file_uuid', $uuid)->where('entry_id', $entryId)->where('state', '!=', 'archived')->first();
            if( !$entryFile )
                continue;

            $entryFile->update([
                'state' => $state,
                'online_patcher' => $onlinePatcher,
                'secondary_online_patcher' => $secondaryOnlinePatcher,
            ]);

            if( section_must_be( ['romhacks', 'translations', 'homebrew'], $this->entry->type ) ) {

                $playOnline = (bool)($metadataArray[$uuid]['play_online'] ?? false);
                $playOnlineCore = $metadataArray[$uuid]['play_online_core'] ?? null;
                $playOnlineThreads = (bool)($metadataArray[$uuid]['play_online_threads'] ?? false);

                if ($playOnline) {
                    if ($playOnlineCore === null || !in_array($playOnlineCore, PlayOnlineHelpers::getCoreLists()))
                        $playOnlineCore = $this->entry->getRealPlatform()->play_online_core ? $this->entry->getRealPlatform()->play_online_core : 'nes';

                    $entryFile->playOnlineSetting()->updateOrCreate(
                        ['file_id' => $entryFile->id],
                        [
                            'core' => $playOnlineCore,
                            'threads' => $playOnlineThreads,
                        ]
                    );
                } else {
                    $entryFile->playOnlineSetting()->delete();
                }
            }
        }
    }

    private function eStep7_UpdateHashes(int $entryId): void
    {
        $requestHashes = collect( $this->request->input('hashes', [] ) )
            ->filter( fn($h) => isset( $h['filename'], $h['hash_crc32'], $h['hash_sha1'], $h['verified'] ) )
            ->keyBy( 'hash_sha1' )
            ->toArray();
        ;

        $existingHashes = EntryHash::where( 'entry_id', $entryId )->get()->keyBy( 'hash_sha1' );

        $hashsToDelete = array_diff( $existingHashes->keys()->toArray(), array_keys( $requestHashes ) );

        if( !empty( $hashsToDelete ) ){
            EntryHash::where( 'entry_id', $entryId )->whereIn('hash_sha1', $hashsToDelete)->delete();
        }

        foreach( $requestHashes as $sha1 => $hash ){
            if( $existingHashes->has( $sha1 ) ){
                $existingHashes->get( $sha1 )->update([
                    'filename' => $hash['filename'],
                    'hash_crc32' => $hash['hash_crc32'],
                    'hash_sha1' => $hash['hash_sha1'],
                    'verified' => $hash['verified'],
                ]);
            } else {
                EntryHash::create([
                    'entry_id' => $entryId,
                    'filename' => $hash['filename'],
                    'hash_crc32' => $hash['hash_crc32'],
                    'hash_sha1' => $hash['hash_sha1'],
                    'verified' => $hash['verified'],
                ]);
            }
        }
    }

    /**
     * @return void
     * @throws SubmissionException
     */
    private function eStep8_UpdateAuthors(): void
    {
        $syncAuthorsId = [];
        $requestAuthorsId = $this->request->input('authors', [] ) ?? [];

        if( !empty( $requestAuthorsId ) ){
            $valid = Author::whereIn( 'id', $requestAuthorsId )->pluck('id')->toArray();

            if( count( $valid ) !== count( $requestAuthorsId ) ){
                throw new SubmissionException( "One of the authors doesn't exist." );
            }

            $syncAuthorsId = array_merge( $syncAuthorsId, $requestAuthorsId );
        }

        foreach ( $this->request->input('new-authors', [] ) ?? [] as $authorName ) {
            $authorName = trim($authorName);
            if ($authorName === '')
                continue;

            $author = Author::firstOrCreate(
                ['slug' => EntryHelpers::uniqueSlug($authorName, Author::class)],
                ['name' => $authorName]
            );

            $syncAuthorsId[] = $author->id;
        }

        $this->entry->authors()->sync( $syncAuthorsId );
    }

    /**
     * @return void
     * @throws SubmissionException
     */
    private function eStep9_UpdateRomhacksModifications(): void
    {
        $requestModifications = $this->request->input('modifications', [] ) ?? [];
        if( !empty( $requestModifications ) ){
            $valid = Modification::whereIn( 'id', $requestModifications )->pluck('id')->toArray();

            if( count( $valid ) !== count( $requestModifications ) ){
                throw new SubmissionException( "One of the modifications doesn't exist." );
            }


        }

        $this->entry->modifications()->sync( $requestModifications );

    }

    /**
     * @return void
     * @throws SubmissionException
     */
    private function eStep9_UpdateUtilitiesSystems(): void
    {
        $requestSystems = $this->request->input('systems', [] ) ?? [];
        if( !empty( $requestSystems ) ){
            $valid = System::whereIn( 'id', $requestSystems )->pluck('id')->toArray();

            if( count( $valid ) !== count( $requestSystems ) ){
                throw new SubmissionException( "One of the systems doesn't exist." );
            }


        }

        $this->entry->systems()->sync( $requestSystems );

    }

    /**
     * @return void
     * @throws SubmissionException
     */
    private function eStep10_UpdateLanguages(): void
    {
        $requestLanguages = $this->request->input('languages', [] ) ?? [];
        if( !empty( $requestLanguages ) ){
            $valid = Language::whereIn( 'id', $requestLanguages )->pluck('id')->toArray();
            if( count( $valid ) !== count( $requestLanguages ) ){
                throw new SubmissionException( "One of the languages doesn't exist." );
            }

        }

        $this->entry->languages()->sync( $requestLanguages );
    }

    /**
     * @return void
     * @throws SubmissionException
     */
    private function eStep10_5_UpdateCategories(): void
    {
        $requestCategories = $this->request->input('categories', [] ) ?? [];
        if( !empty( $requestCategories ) ){
            $valid = Category::whereIn( 'id', $requestCategories )->pluck('id')->toArray();
            if( count( $valid ) !== count( $requestCategories ) ){
                throw new SubmissionException( "One of the categories doesn't exist." );
            }

        }

        $this->entry->categories()->sync( $requestCategories );
    }

    private function eStep11a_UpdateGalleryImages(): array
    {
        $requestGallery = $this->request->input('gallery', [] ) ?? [];
        $existingGalleryPaths = $this->entry->gallery->pluck('image')->toArray();

        $needDeletion = array_diff( $existingGalleryPaths, $requestGallery );

        if( !empty( $needDeletion ) ){
            $this->entry->gallery()->whereIn('image', $needDeletion )->delete();
        }

        $needAddition = array_diff( $requestGallery, $existingGalleryPaths );
        $images = [];
        foreach( $needAddition as $imagePath ){
            $images[] = $this->entry->gallery()->create([
                'image' => $imagePath,
            ]);
        }

        foreach ( $requestGallery as $i => $imagePath ){
            $this->entry->gallery()->where('image', $imagePath )->update(['order' => $i]);
        }

        return [ 'addition' => $images, 'deletion' => $needDeletion ];
    }

    private function eStep11b_UpdateMainImage( ?string $oldMainImagePath ): void
    {
        $currentMainImagePath = $this->entry->main_image;

        if( $currentMainImagePath === $oldMainImagePath )
            return;

        if( !$currentMainImagePath ) {
            if( $oldMainImagePath && Storage::disk('public')->exists($oldMainImagePath) )
                Storage::disk('public')->delete($oldMainImagePath);
            return;
        }

        $newPath = 'entries/main-images/' . basename( $currentMainImagePath );

        if( !Storage::disk('public')->move( $currentMainImagePath, $newPath ) ){
            $this->entry->update(['main_image' => $oldMainImagePath]);
            return;
        }

        $this->entry->update(['main_image' => $newPath]);
        if( $oldMainImagePath && Storage::disk('public')->exists($oldMainImagePath) )
            Storage::disk('public')->delete($oldMainImagePath);
    }

    private function eStep11c_UpdateGalleryImages( array $pathsChanges ): void
    {
        foreach ( $pathsChanges['deletion'] as $deletePath ){
            if( Storage::disk('public')->exists($deletePath) )
                Storage::disk('public')->delete($deletePath);
        }

        foreach ( $pathsChanges['addition'] as $galleryItem ){
            $newPath = 'entries/gallery-images/' . $this->entry->id . '/' . basename( $galleryItem->image );

            if( !Storage::disk('public')->move( $galleryItem->image, $newPath ) ){
                continue;
            }

            $galleryItem->update(['image' => $newPath]);
        }
    }

    private function Step13_CreateCommentsThread( Entry $entry ): void
    {
        if( $entry->state !== 'published' )
            return;

        if( !$entry->comments_thread_id )
            CreateXenForoCommentsThread::dispatch( $entry );
            // app(XenforoApiService::class)->createCommentsThread( $entry );
    }

}
