<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Category;
use App\Models\Collection;
use App\Models\User;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;

class CollectionSearchService
{
    public function publicSearch(array $filters, int $perPage = 18): LengthAwarePaginator
    {
        $query = Collection::query()
            ->publicEligible()
            ->with(['user:id,username,name', 'coverArtwork:id,user_id,title,slug,hash,thumb_ext,published_at,is_public,is_approved,deleted_at']);

        $this->applySharedFilters($query, $filters, false);
        $this->applyPublicSort($query, (string) ($filters['sort'] ?? 'trending'));

        return $query->paginate(max(1, min($perPage, 24)))->withQueryString();
    }

    public function ownerSearch(User $user, array $filters, int $perPage = 20): LengthAwarePaginator
    {
        $query = Collection::query()
            ->with(['user:id,username,name', 'coverArtwork:id,user_id,title,slug,hash,thumb_ext,published_at,is_public,is_approved,deleted_at'])
            ->when(! $user->isAdmin() && ! $user->isModerator(), fn ($builder) => $builder->where('user_id', $user->id));

        $this->applySharedFilters($query, $filters, true);

        return $query
            ->orderByDesc('updated_at')
            ->paginate(max(1, min($perPage, 50)))
            ->withQueryString();
    }

    public function publicFilterOptions(): array
    {
        $themeOptions = Collection::query()
            ->publicEligible()
            ->whereNotNull('theme_token')
            ->where('theme_token', '!=', '')
            ->select('theme_token')
            ->distinct()
            ->orderBy('theme_token')
            ->limit(12)
            ->pluck('theme_token')
            ->map(fn ($token): array => [
                'value' => (string) $token,
                'label' => $this->humanizeToken((string) $token),
            ])
            ->values()
            ->all();

        $categoryOptions = Category::query()
            ->active()
            ->orderBy('sort_order')
            ->orderBy('name')
            ->limit(16)
            ->get(['slug', 'name'])
            ->map(fn (Category $category): array => [
                'value' => (string) $category->slug,
                'label' => (string) $category->name,
            ])
            ->values()
            ->all();

        $styleOptions = collect((array) config('collections.smart_rules.style_terms', []))
            ->map(fn ($term): array => [
                'value' => (string) $term,
                'label' => $this->humanizeToken((string) $term),
            ])
            ->values()
            ->all();

        $colorOptions = collect((array) config('collections.smart_rules.color_terms', []))
            ->map(fn ($term): array => [
                'value' => (string) $term,
                'label' => $this->humanizeToken((string) $term),
            ])
            ->values()
            ->all();

        return [
            'category' => $categoryOptions,
            'style' => $styleOptions,
            'theme' => $themeOptions,
            'color' => $colorOptions,
            'quality_tier' => [
                ['value' => 'editorial', 'label' => 'Editorial'],
                ['value' => 'high', 'label' => 'High'],
                ['value' => 'standard', 'label' => 'Standard'],
                ['value' => 'limited', 'label' => 'Limited'],
            ],
        ];
    }

    private function applySharedFilters($query, array $filters, bool $includeInternal): void
    {
        if (filled($filters['q'] ?? null)) {
            $term = '%' . trim((string) $filters['q']) . '%';
            $query->where(function ($builder) use ($term): void {
                $builder->where('title', 'like', $term)
                    ->orWhere('summary', 'like', $term)
                    ->orWhere('description', 'like', $term)
                    ->orWhere('campaign_label', 'like', $term)
                    ->orWhere('series_title', 'like', $term)
                    ->orWhereHas('user', function (Builder $userQuery) use ($term): void {
                        $userQuery->where('username', 'like', $term)
                            ->orWhere('name', 'like', $term);
                    });
            });
        }

        foreach (['type', 'visibility', 'lifecycle_state', 'mode', 'campaign_key', 'program_key', 'workflow_state', 'health_state'] as $field) {
            if (filled($filters[$field] ?? null)) {
                $query->where($field, (string) $filters[$field]);
            }
        }

        if (filled($filters['quality_tier'] ?? null)) {
            $query->where('trust_tier', (string) $filters['quality_tier']);
        }

        if (filled($filters['theme'] ?? null)) {
            $theme = trim((string) $filters['theme']);
            $themeLike = '%' . mb_strtolower($theme) . '%';

            $query->where(function (Builder $builder) use ($theme, $themeLike): void {
                $builder->whereRaw('LOWER(theme_token) = ?', [mb_strtolower($theme)])
                    ->orWhereHas('entityLinks', function (Builder $linkQuery) use ($themeLike): void {
                        $linkQuery->where('linked_type', CollectionLinkService::TYPE_TAG)
                            ->whereExists(function ($tagQuery) use ($themeLike): void {
                                $tagQuery->select(DB::raw('1'))
                                    ->from('tags')
                                    ->whereColumn('tags.id', 'collection_entity_links.linked_id')
                                    ->where(function ($tagBuilder) use ($themeLike): void {
                                        $tagBuilder->whereRaw('LOWER(tags.slug) like ?', [$themeLike])
                                            ->orWhereRaw('LOWER(tags.name) like ?', [$themeLike]);
                                    });
                            });
                    })
                    ->orWhereHas('artworks.tags', function (Builder $tagQuery) use ($themeLike): void {
                        $tagQuery->whereRaw('LOWER(tags.slug) like ?', [$themeLike])
                            ->orWhereRaw('LOWER(tags.name) like ?', [$themeLike]);
                    });
            });
        }

        if (filled($filters['category'] ?? null)) {
            $category = trim((string) $filters['category']);
            $categoryLike = '%' . mb_strtolower($category) . '%';

            $query->where(function (Builder $builder) use ($categoryLike): void {
                $builder->whereHas('entityLinks', function (Builder $linkQuery) use ($categoryLike): void {
                    $linkQuery->where('linked_type', CollectionLinkService::TYPE_CATEGORY)
                        ->whereExists(function ($categoryQuery) use ($categoryLike): void {
                            $categoryQuery->select(DB::raw('1'))
                                ->from('categories')
                                ->whereColumn('categories.id', 'collection_entity_links.linked_id')
                                ->where(function ($inner) use ($categoryLike): void {
                                    $inner->whereRaw('LOWER(categories.slug) like ?', [$categoryLike])
                                        ->orWhereRaw('LOWER(categories.name) like ?', [$categoryLike]);
                                });
                        });
                })->orWhereHas('artworks.categories', function (Builder $categoryQuery) use ($categoryLike): void {
                    $categoryQuery->whereRaw('LOWER(categories.slug) like ?', [$categoryLike])
                        ->orWhereRaw('LOWER(categories.name) like ?', [$categoryLike]);
                });
            });
        }

        if (filled($filters['style'] ?? null)) {
            $style = trim((string) $filters['style']);
            $styleLike = '%' . mb_strtolower($style) . '%';

            $query->where(function (Builder $builder) use ($styleLike): void {
                $builder->whereRaw('LOWER(spotlight_style) like ?', [$styleLike])
                    ->orWhereRaw('LOWER(presentation_style) like ?', [$styleLike])
                    ->orWhereHas('artworks.tags', function (Builder $tagQuery) use ($styleLike): void {
                        $tagQuery->whereRaw('LOWER(tags.slug) like ?', [$styleLike])
                            ->orWhereRaw('LOWER(tags.name) like ?', [$styleLike]);
                    });
            });
        }

        if (filled($filters['color'] ?? null)) {
            $color = trim((string) $filters['color']);
            $colorLike = '%' . mb_strtolower($color) . '%';

            $query->where(function (Builder $builder) use ($colorLike): void {
                $builder->whereRaw('LOWER(theme_token) like ?', [$colorLike])
                    ->orWhereHas('artworks.tags', function (Builder $tagQuery) use ($colorLike): void {
                        $tagQuery->whereRaw('LOWER(tags.slug) like ?', [$colorLike])
                            ->orWhereRaw('LOWER(tags.name) like ?', [$colorLike]);
                    });
            });
        }

        if (filled($filters['placement_eligibility'] ?? null) && $includeInternal) {
            $query->where('placement_eligibility', filter_var($filters['placement_eligibility'], FILTER_VALIDATE_BOOLEAN));
        }

        if (filled($filters['partner_key'] ?? null) && $includeInternal) {
            $query->where('partner_key', (string) $filters['partner_key']);
        }

        if (filled($filters['experiment_key'] ?? null) && $includeInternal) {
            $query->where('experiment_key', (string) $filters['experiment_key']);
        }
    }

    private function applyPublicSort($query, string $sort): void
    {
        match ($sort) {
            'recent' => $query->orderByDesc('published_at')->orderByDesc('updated_at'),
            'quality' => $query->orderByDesc('health_score')->orderByDesc('quality_score'),
            'evergreen' => $query->orderByDesc('quality_score')->orderByDesc('followers_count'),
            default => $query->orderByDesc('ranking_score')->orderByDesc('health_score')->orderByDesc('updated_at'),
        };
    }

    private function humanizeToken(string $value): string
    {
        return str($value)
            ->replace(['_', '-'], ' ')
            ->title()
            ->value();
    }
}