<?php

namespace App\Sharp\Posts;

use App\Models\Post;
use App\Models\PostAttachment;
use App\Sharp\Entities\PostEntity;
use App\Sharp\Posts\Commands\BulkPublishPostsCommand;
use App\Sharp\Posts\Commands\ComposeEmailWithPostsWizardCommand;
use App\Sharp\Posts\Commands\EvaluateDraftPostWizardCommand;
use App\Sharp\Posts\Commands\PreviewPostCommand;
use App\Sharp\Utils\DateTimeCustomTransformer;
use App\Sharp\Utils\Filters\AuthorFilter;
use App\Sharp\Utils\Filters\CategoryFilter;
use App\Sharp\Utils\Filters\PeriodFilter;
use App\Sharp\Utils\Filters\PostAttachmentFilter;
use App\Sharp\Utils\Filters\StateFilter;
use Code16\Sharp\EntityList\Fields\EntityListBadgeField;
use Code16\Sharp\EntityList\Fields\EntityListField;
use Code16\Sharp\EntityList\Fields\EntityListFieldsContainer;
use Code16\Sharp\EntityList\Fields\EntityListStateField;
use Code16\Sharp\EntityList\SharpEntityList;
use Code16\Sharp\Filters\DateRange\DateRangeFilterValue;
use Code16\Sharp\Utils\Links\LinkToEntityList;
use Code16\Sharp\Utils\PageAlerts\PageAlert;
use Code16\Sharp\Utils\Transformers\Attributes\Eloquent\SharpTagsTransformer;
use Code16\Sharp\Utils\Transformers\Attributes\Eloquent\SharpUploadModelThumbnailUrlTransformer;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Builder;

class PostList extends SharpEntityList
{
    protected function buildList(EntityListFieldsContainer $fields): void
    {
        $fields
            ->addField(
                EntityListBadgeField::make('is_draft')
                    ->setTooltip('This post is draft')
            )
            ->addField(
                EntityListField::make('cover')
                    ->setWidth(.1)
                    ->hideOnSmallScreens(),
            )
            ->addField(
                EntityListField::make('title')
                    ->setLabel('Title')
            )
            ->addField(
                EntityListStateField::make()
            )
            ->addField(
                EntityListField::make('author:name')
                    ->setLabel('Author')
                    ->hideOnSmallScreens()
                    ->setSortable(),
            )
            ->addField(
                EntityListField::make('categories')
                    ->setLabel('Categories')
                    ->hideOnSmallScreens()
            )
            ->addField(
                EntityListField::make('published_at')
                    ->setLabel('Published at')
                    ->setWidth(.2)
                    ->setSortable(),
            );
    }

    public function buildListConfig(): void
    {
        $this
            ->configureCreateButtonLabel('New post...')
            ->configureEntityState('state', PostStateHandler::class)
            ->configureDefaultSort('published_at', 'desc')
            ->configureDelete(confirmationText: 'Are you sure you want to delete this post (this will permanently delete its data)?')
            ->configureSearchable();
    }

    protected function buildPageAlert(PageAlert $pageAlert): void
    {
        if (sharp()->context()->isShow()) {
            return;
        }

        if (auth()->user()->isAdmin() && ($count = Post::where('state', 'draft')->count()) > 0) {
            $pageAlert
                ->setMessage(sprintf('%d posts are still in draft', $count))
                ->setButton(
                    'Show drafts',
                    LinkToEntityList::make(PostEntity::class)->addFilter(StateFilter::class, 'draft')
                );
        }

        if (! auth()->user()->isAdmin()) {
            $pageAlert
                ->setMessage('As an editor, you can only edit your posts; you can see other posts except those which are still in draft.')
                ->setLevelInfo();
        }
    }

    protected function getFilters(): ?array
    {
        return [
            StateFilter::class,
            AuthorFilter::class,
            CategoryFilter::class,
            PostAttachmentFilter::class,
            PeriodFilter::class,
        ];
    }

    public function getInstanceCommands(): ?array
    {
        return [
            PreviewPostCommand::class,
            EvaluateDraftPostWizardCommand::class,
        ];
    }

    protected function getEntityCommands(): ?array
    {
        return [
            ComposeEmailWithPostsWizardCommand::class,
            BulkPublishPostsCommand::class,
        ];
    }

    public function getListData(): array|Arrayable
    {
        $posts = Post::select('posts.*')
            ->with(['author', 'categories', 'cover'])

            // Handle specific IDs (in case of refresh, called by a state handler or a command)
            ->when(
                $this->queryParams->specificIds(),
                fn (Builder $builder, array $ids) => $builder->whereIn('id', $ids),
            )

            // Handle filters
            ->when(
                $this->queryParams->filterFor(StateFilter::class),
                fn (Builder $builder, string $state) => $builder->where('state', $state),
            )
            ->when(
                $this->queryParams->filterFor(PeriodFilter::class),
                fn (Builder $builder, DateRangeFilterValue $dates) => $builder->whereBetween(
                    'published_at',
                    [$dates->getStart(), $dates->getEnd()]
                ),
            )
            ->when(
                $this->queryParams->filterFor(AuthorFilter::class),
                fn (Builder $builder, int $authorId) => $builder->where('author_id', $authorId),
            )
            ->when(
                $this->queryParams->filterFor(CategoryFilter::class),
                function (Builder $builder, $categories) {
                    $builder->where(function (Builder $builder) use ($categories) {
                        collect($categories)
                            ->each(function ($categoryId) use ($builder) {
                                $builder->orWhereHas(
                                    'categories',
                                    fn (Builder $builder) => $builder->where('categories.id', $categoryId)
                                );
                            });
                    });
                },
            )
            ->when(
                $this->queryParams->filterFor(PostAttachmentFilter::class),
                function (Builder $builder, int $attachmentId) {
                    $builder->whereHas('attachments', function (Builder $builder) use ($attachmentId) {
                        $builder->where('title', PostAttachment::find($attachmentId)->title);
                    });
                },
            )

            // Handle search words
            ->when(
                $this->queryParams->hasSearch(),
                function (Builder $builder) {
                    foreach ($this->queryParams->searchWords() as $word) {
                        $builder->where(function ($query) use ($word) {
                            $query
                                ->orWhere('title->fr', 'like', $word)
                                ->orWhere('title->en', 'like', $word);
                        });
                    }
                },
            )

            // Handle sorting
            ->when(
                $this->queryParams->sortedBy() === 'author:name',
                fn (Builder $builder) => $builder
                    ->leftJoin('users', 'posts.author_id', '=', 'users.id')
                    ->orderBy('users.name', $this->queryParams->sortedDir()),
                fn (Builder $builder) => $builder->orderBy('published_at', $this->queryParams->sortedDir() ?: 'desc')
            );

        return $this
            ->setCustomTransformer('is_draft', fn ($value, Post $instance) => $instance->isDraft())
            ->setCustomTransformer('title', function ($value, Post $instance) {
                return sprintf(
                    '<div>%s</div><div><small>[fr] %s</div>',
                    $instance->getTranslation('title', 'en'),
                    $instance->getTranslation('title', 'fr')
                );
            })
            ->setCustomTransformer('author:name', function ($value, $instance) {
                return $value
                    ? LinkToEntityList::make(PostEntity::class)
                        ->addFilter(AuthorFilter::class, $instance->id)
                        ->setTooltip('See '.$value.' posts')
                        ->renderAsText($value)
                    : null;
            })
            ->setCustomTransformer('cover', new SharpUploadModelThumbnailUrlTransformer(100)->renderAsImageTag())
            ->setCustomTransformer('published_at', DateTimeCustomTransformer::class)
            ->setCustomTransformer('categories', new SharpTagsTransformer('name')->setFilterLink(PostEntity::class, CategoryFilter::class))
            ->transform($posts->paginate(20));
    }
}
