<?php

namespace App\Livewire;

use App\Models\Author;
use App\Models\Category;
use App\Models\Entry;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Language;
use App\Models\Level;
use App\Models\Modification;
use App\Models\Platform;
use App\Models\Status;
use App\Models\System;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;

class Database extends Component
{
    use WithPagination;

    /**
     * entry_title search
     * @var string
     */
    #[Url(as: 's',except: '')]
    public string $search = '';

    /**
     * type filter.
     * @var array
     */
    #[Url(except:[])]
    public array $types = [];

    /**
     * Games IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $games = [];

    /**
     * Current game search
     * @var string
     */
    public string $gameSearch = '';

    /**
     * Platform IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $platforms = [];

    /**
     * Genre IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $genres = [];

    /**
     * Status IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $statuses = [];

    /**
     * Authors IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $authors = [];

    /**
     * Authors mode and/or.
     * @var string
     */
    #[Url(except:'or')]
    public string $authorsMode = 'or';

    /**
     * Current author search.
     * @var string
     */
    public string $authorSearch = '';

    /**
     * Languages IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $languages = [];

    /**
     * Languages mode and/or
     * @var string
     */
    #[Url(except:'or')]
    public string $languagesMode = 'or';

    /**
     * Modifications IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $modifications = [];

    /**
     * Modifications mode and/or.
     * @var string
     */
    #[Url(except:'or')]
    public string $modificationsMode = 'or';

    /**
     * Categories IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $categories = [];

    /**
     * Categories mode and/or
     * @var string
     */
    #[Url(except:'or')]
    public string $categoriesMode = 'or';

    /**
     * Systems IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $systems = [];

    /**
     * Systems mode and/or
     * @var string
     */
    #[Url(except:'or')]
    public string $systemsMode = 'or';

    /**
     * Levels IDs filter.
     * @var array
     */
    #[Url(except:[])]
    public array $levels = [];

    #[Url(except:null)]
    public ?int $userId = null;

    /**
     * Sort by field.
     * @var string
     */
    #[Url(as: 'sort',except: 'created_at')]
    public string $sortBy = 'created_at';

    /**
     * asc/desc
     * @var string
     */
    #[Url(as: 'dir',except: 'desc')]
    public string $sortDir = 'desc';

    /**
     * Translation of sort options key.
     */
    public const array SORT_OPTIONS = [
        'created_at' => 'Date added',
        'release_date' => 'Release date',
        'title' => 'Title'
    ];

    /**
     * Translation of entries key.
     */
    public const array ENTRY_TYPES = [
        'translations' => 'Translations',
        'romhacks' => 'Romhacks',
        'homebrew' => 'Homebrew',
        'utilities' => 'Utilities',
        'documents' => 'Documents',
        'lua-scripts' => 'Lua Scripts',
    ];

    public const int PAGINATION = 30;

    public function updatedSearch():        void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedTypes():         void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedGames():         void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedPlatforms():     void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedGenres():        void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedStatuses():      void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedAuthors():       void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedAuthorsMode():   void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedLanguages():     void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedLanguagesMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedModifications(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedModificationsMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedCategories():     void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedCategoriesMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedSystems():        void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedSystemsMode():    void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedLevels():        void { $this->resetPage(); $this->dispatch('filters-updated'); }
    public function updatedUserId():        void { $this->resetPage(); $this->dispatch('filters-updated'); }

    public function clearFilters(): void
    {
        $this->reset([
            'search', 'types', 'platforms', 'genres', 'statuses', 'authors', 'authorsMode', 'languages', 'languagesMode', 'modifications', 'modificationsMode', 'categories', 'categoriesMode', 'systems', 'systemsMode', 'levels', 'userId'
        ]);
        $this->dispatch('filters-updated');
        $this->resetPage();
    }

    public function setSort(string $field): void
    {
        if( $this->sortBy === $field ) {
            $this->sortDir = $this->sortDir === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortBy = $field;
            $this->sortDir = 'asc';
        }
        $this->resetPage();
        $this->dispatch('filters-updated');
    }

    private function buildQuery()
    {
        $query = Entry::query()->published()->with([
            'game.platform', 'game.genre', 'status', 'authors', 'languages', 'level', 'systems', 'categories', 'modifications'
        ]);

        if( $this->search ) {
            $query->where(function($q) {
               $q->where('title', 'like', '%'.$this->search.'%');
               $q->orWhere('complete_title', 'like', '%'.$this->search.'%');
            });
        }

        if( $this->types ) {
            $query->whereIn('type', $this->types);
        }

        if( $this->platforms ) {
            $query->where(function($q) {
                $q->whereIn('platform_id', $this->platforms)
                ->orWhereHas('game', fn($q2) => $q2->whereIn('platform_id', $this->platforms) );
            });
        }

        if( $this->genres ) {
            $query->where(function($q) {
                $q->whereHas('game', fn($q2) => $q2->whereIn('genre_id', $this->genres) );
            });
        }

        if( $this->games ){
            $query->whereIn('game_id', $this->games);
        }

        if( $this->statuses ) {
            $query->whereIn('status_id', $this->statuses);
        }

        if( $this->authors ) {
            if( $this->authorsMode === 'and' ) {
                foreach ( $this->authors as $authorId ) {
                    $query->whereHas('authors', fn($q) => $q->where('authors.id', $authorId));
                }
            } else {
                $query->whereHas('authors', fn($q) => $q->whereIn('authors.id', $this->authors));
            }
        }

        if( $this->languages ) {
            if( $this->languagesMode === 'and' ) {
                foreach ( $this->languages as $langId ) {
                    $query->whereHas('languages', fn($q) => $q->where('languages.id', $langId));
                }
            } else {
                $query->whereHas('languages', fn($q) => $q->whereIn('languages.id', $this->languages));
            }
        }

        if( $this->modifications ) {
            if( $this->modificationsMode === 'and' ) {
                foreach ( $this->modifications as $modificationId ) {
                    $query->whereHas('modifications', fn($q) => $q->where('modifications.id', $modificationId));
                }
            } else {
                $query->whereHas('modifications', fn($q) => $q->whereIn('modifications.id', $this->modifications));
            }
        }

        if( $this->levels ) {
            $query->whereIn('level_id', $this->levels);
        }

        if( $this->categories ) {
            if( $this->categoriesMode === 'and' ) {
                foreach ( $this->categories as $categoryId ) {
                    $query->whereHas('categories', fn($q) => $q->where('categories.id', $categoryId));
                }
            } else {
                $query->whereHas('categories', fn($q) => $q->whereIn('categories.id', $this->categories));
            }
        }

        if( $this->systems ) {
            if( $this->systemsMode === 'and' ) {
                foreach ( $this->systems as $systemId ) {
                    $query->whereHas('systems', fn($q) => $q->where('systems.id', $systemId));
                }
            } else {
                $query->whereHas('systems', fn($q) => $q->whereIn('systems.id', $this->systems));
            }
        }

        if( $this->userId ){
            $query->where('user_id', $this->userId);
        }

        return $query->orderBy($this->sortBy, $this->sortDir);
    }

    private function searchFilter( string $modelClass, string $search )
    {

        $this->dispatch('filters-updated');

        if( mb_strlen( $search ) < 3 ) {
            return collect();
        }

        return $modelClass::where('name', 'like', "%{$search}%")
            ->orderBy('name')
            ->limit(50)
            ->get();
    }

    private function searchGameFilter()
    {

        $search = $this->gameSearch;

        $this->dispatch('filters-updated');

        if( mb_strlen( $search ) < 3 ) {
            return collect();
        }

        $collect = Game::where('name', 'like', "%{$search}%")
            ->orderBy('name')
            ->limit(50)
            ->get();

        return $collect->map(function($item){
            $item->name = $item->name . ' (' . ($item->platform?->short_name ?? $item->platform->name) . ')';
            return $item;
        } );
    }

    public function render()
    {
        return view('livewire.database', [
            'entries' => $this->buildQuery()->paginate(self::PAGINATION),
            'allGames' => $this->searchGameFilter(),
            'allPlatforms' => Platform::orderBy('name')->get(),
            'allGenres' => Genre::orderBy('name')->get(),
            'allStatuses' => Status::orderBy('name')->get(),
            'allAuthors' => $this->searchFilter(Author::class, $this->authorSearch),
            'allLanguages' => Language::orderBy('name')->get(),
            'allModifications' => Modification::orderBy('name')->get(),
            'allCategories' => Category::orderBy('name')->get(),
            'allLevels' => Level::orderBy('name')->get(),
            'allSystems' => System::orderBy('name')->get(),
        ]);
    }
}
