<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Artwork;
use App\Models\Tag;
use App\Services\EarlyGrowth\AdaptiveTimeWindow;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Cache;

/**
 * High-level search API powered by Meilisearch via Laravel Scout.
 *
 * No Meili calls in controllers — always go through this service.
 */
final class ArtworkSearchService
{
    private const BASE_FILTER = 'is_public = true AND is_approved = true';
    private const CACHE_TTL   = 300; // 5 minutes

    public function __construct(
        private readonly AdaptiveTimeWindow $timeWindow,
    ) {}

    /**
     * Full-text search with optional filters.
     *
     * Supported $filters keys:
     *   tags        array<string>  — tag slugs (AND match)
     *   category    string
     *   orientation string         — landscape | portrait | square
     *   resolution  string         — e.g. "1920x1080"
     *   author_id   int
     *   sort        string         — created_at|downloads|likes|views (suffix :asc or :desc)
     */
    public function search(string $q, array $filters = [], int $perPage = 24): LengthAwarePaginator
    {
        $filterParts = [self::BASE_FILTER];
        $sort        = [];

        if (! empty($filters['tags'])) {
            foreach ((array) $filters['tags'] as $tag) {
                $filterParts[] = 'tags = "' . addslashes((string) $tag) . '"';
            }
        }

        if (! empty($filters['category'])) {
            $filterParts[] = 'category = "' . addslashes((string) $filters['category']) . '"';
        }

        if (! empty($filters['orientation'])) {
            $filterParts[] = 'orientation = "' . addslashes((string) $filters['orientation']) . '"';
        }

        if (! empty($filters['resolution'])) {
            $filterParts[] = 'resolution = "' . addslashes((string) $filters['resolution']) . '"';
        }

        if (! empty($filters['author_id'])) {
            $filterParts[] = 'author_id = ' . (int) $filters['author_id'];
        }

        if (! empty($filters['sort'])) {
            [$field, $dir] = $this->parseSort((string) $filters['sort']);
            if ($field) {
                $sort[] = $field . ':' . $dir;
            }
        }

        $options = ['filter' => implode(' AND ', $filterParts)];
        if ($sort !== []) {
            $options['sort'] = $sort;
        }

        return Artwork::search($q ?: '')
            ->options($options)
            ->paginate($perPage);
    }

    /**
     * Load artworks for a tag page, sorted by views + likes descending.
     */
    public function byTag(string $slug, int $perPage = 24): LengthAwarePaginator
    {
        $tag = Tag::where('slug', $slug)->first();
        if (! $tag) {
            return $this->emptyPaginator($perPage);
        }

        $cacheKey = "search.tag.{$slug}.page." . request()->get('page', 1);

        return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($slug, $perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND tags = "' . addslashes($slug) . '"',
                    'sort'   => ['views:desc', 'likes:desc'],
                ])
                ->paginate($perPage);
        });
    }

    /**
     * Load artworks for a category, sorted by created_at desc.
     */
    public function byCategory(string $cat, int $perPage = 24, array $filters = []): LengthAwarePaginator
    {
        $cacheKey = "search.cat.{$cat}.page." . request()->get('page', 1);

        return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($cat, $perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND category = "' . addslashes($cat) . '"',
                    'sort'   => ['created_at:desc'],
                ])
                ->paginate($perPage);
        });
    }

    // ── Category / Content-Type page sorts ────────────────────────────────────

    /**
     * Meilisearch sort fields per alias.
     * Used by categoryPageSort() and contentTypePageSort().
     */
    private const CATEGORY_SORT_FIELDS = [
        'trending'   => ['trending_score_24h:desc', 'created_at:desc'],
        'fresh'      => ['created_at:desc'],
        'top-rated'  => ['awards_received_count:desc', 'favorites_count:desc'],
        'favorited'  => ['favorites_count:desc', 'trending_score_24h:desc'],
        'downloaded' => ['downloads_count:desc', 'trending_score_24h:desc'],
        'oldest'     => ['created_at:asc'],
    ];

    /** Cache TTL (seconds) per sort alias for category pages. */
    private const CATEGORY_SORT_TTL = [
        'trending'   => 300,   // 5 min
        'fresh'      => 120,   // 2 min
        'top-rated'  => 600,   // 10 min
        'favorited'  => 300,
        'downloaded' => 300,
        'oldest'     => 600,
    ];

    /**
     * Artworks for a single category page, sorted via Meilisearch.
     * Default sort: trending (trending_score_24h:desc).
     *
     * Cache key pattern: category.{slug}.{sort}.{page}
     * TTL varies by sort (see spec: 5/2/10 min).
     */
    public function categoryPageSort(string $categorySlug, string $sort = 'trending', int $perPage = 24): LengthAwarePaginator
    {
        $sort    = array_key_exists($sort, self::CATEGORY_SORT_FIELDS) ? $sort : 'trending';
        $page    = (int) request()->get('page', 1);
        $ttl     = self::CATEGORY_SORT_TTL[$sort] ?? self::CACHE_TTL;
        $cacheKey = "category.{$categorySlug}.{$sort}.{$page}";

        return Cache::remember($cacheKey, $ttl, function () use ($categorySlug, $sort, $perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND category = "' . addslashes($categorySlug) . '"',
                    'sort'   => self::CATEGORY_SORT_FIELDS[$sort],
                ])
                ->paginate($perPage);
        });
    }

    /**
     * Artworks for a content-type root page, sorted via Meilisearch.
     * Default sort: trending.
     *
     * Cache key pattern: content_type.{slug}.{sort}.{page}
     */
    public function contentTypePageSort(string $contentTypeSlug, string $sort = 'trending', int $perPage = 24): LengthAwarePaginator
    {
        $sort    = array_key_exists($sort, self::CATEGORY_SORT_FIELDS) ? $sort : 'trending';
        $page    = (int) request()->get('page', 1);
        $ttl     = self::CATEGORY_SORT_TTL[$sort] ?? self::CACHE_TTL;
        $cacheKey = "content_type.{$contentTypeSlug}.{$sort}.{$page}";

        return Cache::remember($cacheKey, $ttl, function () use ($contentTypeSlug, $sort, $perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND content_type = "' . addslashes($contentTypeSlug) . '"',
                    'sort'   => self::CATEGORY_SORT_FIELDS[$sort],
                ])
                ->paginate($perPage);
        });
    }

    // -------------------------------------------------------------------------

    /**
     * Related artworks: same tags, different artwork, ranked by views + likes.
     * Limit 12.
     */
    public function related(Artwork $artwork, int $limit = 12): LengthAwarePaginator
    {
        $tags = $artwork->tags()->pluck('tags.slug')->values()->all();

        if ($tags === []) {
            return $this->popular($limit);
        }

        $cacheKey = "search.related.{$artwork->id}";

        return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($artwork, $tags, $limit) {
            $tagFilters = implode(' OR ', array_map(
                fn ($t) => 'tags = "' . addslashes($t) . '"',
                $tags
            ));

            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND id != ' . $artwork->id . ' AND (' . $tagFilters . ')',
                    'sort'   => ['views:desc', 'likes:desc'],
                ])
                ->paginate($limit);
        });
    }

    /**
     * Most popular artworks by views.
     */
    public function popular(int $perPage = 24): LengthAwarePaginator
    {
        return Cache::remember('search.popular.page.' . request()->get('page', 1), self::CACHE_TTL, function () use ($perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER,
                    'sort'   => ['views:desc', 'likes:desc'],
                ])
                ->paginate($perPage);
        });
    }

    /**
     * Most recent artworks by created_at.
     */
    public function recent(int $perPage = 24): LengthAwarePaginator
    {
        return Cache::remember('search.recent.page.' . request()->get('page', 1), self::CACHE_TTL, function () use ($perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER,
                    'sort'   => ['created_at:desc'],
                ])
                ->paginate($perPage);
        });
    }

    // ── Discover section helpers ───────────────────────────────────────────────

    /**
     * Trending: sorted by Ranking Engine V2 `ranking_score` (recalculated every 30 min).
     *
     * Spec §6: Uses ranking_score, limits to last 30 days,
     * highlights high-velocity artworks via engagement_velocity tiebreaker.
     */
    public function discoverTrending(int $perPage = 24): LengthAwarePaginator
    {
        $page        = (int) request()->get('page', 1);
        $windowDays  = $this->timeWindow->getTrendingWindowDays(30);
        $cutoff      = now()->subDays($windowDays)->toDateString();
        // Include window in cache key so adaptive expansions surface immediately
        $cacheKey = "discover.trending.{$windowDays}d.{$page}";

        return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($perPage, $cutoff) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND created_at >= "' . $cutoff . '"',
                    'sort'   => ['ranking_score:desc', 'engagement_velocity:desc', 'views:desc'],
                ])
                ->paginate($perPage);
        });
    }

    /**
     * Rising: sorted by heat_score (recalculated every 15 min).
     *
     * Surfaces artworks with rapid recent engagement growth.
     * Restricts to last 30 days, sorted by heat_score DESC.
     */
    public function discoverRising(int $perPage = 24): LengthAwarePaginator
    {
        $page        = (int) request()->get('page', 1);
        $windowDays  = $this->timeWindow->getTrendingWindowDays(30);
        $cutoff      = now()->subDays($windowDays)->toDateString();
        $cacheKey    = "discover.rising.{$windowDays}d.{$page}";

        return Cache::remember($cacheKey, 120, function () use ($perPage, $cutoff) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND created_at >= "' . $cutoff . '"',
                    'sort'   => ['heat_score:desc', 'engagement_velocity:desc', 'created_at:desc'],
                ])
                ->paginate($perPage);
        });
    }

    /**
     * Fresh: newest uploads first.
     */
    public function discoverFresh(int $perPage = 24): LengthAwarePaginator
    {
        $page = (int) request()->get('page', 1);
        return Cache::remember("discover.fresh.{$page}", self::CACHE_TTL, function () use ($perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER,
                    'sort'   => ['created_at:desc'],
                ])
                ->paginate($perPage);
        });
    }

    /**
     * Top rated: highest number of favourites/likes.
     */
    public function discoverTopRated(int $perPage = 24): LengthAwarePaginator
    {
        $page = (int) request()->get('page', 1);
        return Cache::remember("discover.top-rated.{$page}", self::CACHE_TTL, function () use ($perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER,
                    'sort'   => ['likes:desc', 'views:desc'],
                ])
                ->paginate($perPage);
        });
    }

    /**
     * Most downloaded: highest download count.
     */
    public function discoverMostDownloaded(int $perPage = 24): LengthAwarePaginator
    {
        $page = (int) request()->get('page', 1);
        return Cache::remember("discover.most-downloaded.{$page}", self::CACHE_TTL, function () use ($perPage) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER,
                    'sort'   => ['downloads:desc', 'views:desc'],
                ])
                ->paginate($perPage);
        });
    }

    /**
     * Artworks matching any of the given tag slugs, sorted by trending score.
     * Used for personalized "Because you like {tags}" homepage section.
     *
     * @param string[] $tagSlugs
     */
    public function discoverByTags(array $tagSlugs, int $limit = 12): LengthAwarePaginator
    {
        if (empty($tagSlugs)) {
            return $this->popular($limit);
        }

        $tagFilter = implode(' OR ', array_map(
            fn (string $t): string => 'tags = "' . addslashes($t) . '"',
            array_slice($tagSlugs, 0, 5)
        ));

        $cacheKey = 'discover.by-tags.' . md5(implode(',', $tagSlugs));

        return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($tagFilter, $limit) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND (' . $tagFilter . ')',
                    'sort'   => ['trending_score_7d:desc', 'likes:desc'],
                ])
                ->paginate($limit);
        });
    }

    /**
     * Fresh artworks in given categories, sorted by created_at desc.
     * Used for personalized "Fresh in your favourite categories" section.
     *
     * @param string[] $categorySlugs
     */
    public function discoverByCategories(array $categorySlugs, int $limit = 12): LengthAwarePaginator
    {
        if (empty($categorySlugs)) {
            return $this->recent($limit);
        }

        $catFilter = implode(' OR ', array_map(
            fn (string $c): string => 'category = "' . addslashes($c) . '"',
            array_slice($categorySlugs, 0, 3)
        ));

        $cacheKey = 'discover.by-cats.' . md5(implode(',', $categorySlugs));

        return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($catFilter, $limit) {
            return Artwork::search('')
                ->options([
                    'filter' => self::BASE_FILTER . ' AND (' . $catFilter . ')',
                    'sort'   => ['created_at:desc'],
                ])
                ->paginate($limit);
        });
    }

    // -------------------------------------------------------------------------

    private function parseSort(string $sort): array
    {
        $allowed = ['created_at', 'downloads', 'likes', 'views'];
        $parts   = explode(':', $sort, 2);
        $field   = $parts[0] ?? '';
        $dir     = strtolower($parts[1] ?? 'desc') === 'asc' ? 'asc' : 'desc';

        return in_array($field, $allowed, true) ? [$field, $dir] : [null, 'desc'];
    }

    private function emptyPaginator(int $perPage): LengthAwarePaginator
    {
        return new \Illuminate\Pagination\LengthAwarePaginator([], 0, $perPage);
    }
}
