<?php

namespace App\Http\Controllers\Web;

use App\Models\Category;
use App\Models\ContentType;
use App\Models\Artwork;
use App\Services\ArtworkSearchService;
use App\Services\ArtworkService;
use App\Services\ContentTypes\ContentTypeSlugResolver;
use App\Services\Maturity\ArtworkMaturityService;
use App\Services\ThumbnailPresenter;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Illuminate\Pagination\AbstractPaginator;
use Illuminate\Pagination\AbstractCursorPaginator;

class BrowseGalleryController extends \App\Http\Controllers\Controller
{
    /**
     * Meilisearch sort-field arrays per sort alias.
     * First element is primary sort; subsequent elements are tie-breakers.
     */
    private const SORT_MAP = [
        // ── Nova sort aliases ─────────────────────────────────────────────────
        // trending_score_24h only covers artworks ≤ 7 days old; use 7d score
        // and favorites_count as fallbacks so older artworks don't all tie at 0.
        'trending'   => ['trending_score_24h:desc', 'trending_score_7d:desc', 'favorites_count:desc', 'created_at:desc'],
        // "New & Hot": 30-day trending window surfaces recently-active artworks.
        'fresh'      => ['trending_score_7d:desc', 'favorites_count:desc', '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'],
        // ── Legacy aliases (backward compat) ──────────────────────────────────
        'latest'     => ['created_at:desc'],
        'popular'    => ['views:desc', 'favorites_count:desc'],
        'liked'      => ['likes:desc', 'favorites_count:desc'],
        'downloads'  => ['downloads:desc', 'downloads_count:desc'],
    ];

    /**
     * Cache TTL (seconds) per sort alias.
     *   trending   → 5 min
     *   fresh      → 2 min
     *   top-rated  → 10 min
     *   others     → 5 min
     */
    private const SORT_TTL_MAP = [
        'trending'   => 300,
        'fresh'      => 120,
        'top-rated'  => 600,
        'favorited'  => 300,
        'downloaded' => 300,
        'oldest'     => 600,
        'latest'     => 120,
        'popular'    => 300,
        'liked'      => 300,
        'downloads'  => 300,
    ];

    /** Human-readable sort options passed to every gallery view. */
    private const SORT_OPTIONS = [
        ['value' => 'trending',   'label' => '🔥 Trending'],
        ['value' => 'fresh',      'label' => '🆕 Fresh'],
        ['value' => 'top-rated',  'label' => '⭐ Top Rated'],
        ['value' => 'favorited',  'label' => '❤️ Most Favorited'],
        ['value' => 'downloaded', 'label' => '⬇ Most Downloaded'],
        ['value' => 'oldest',     'label' => '📅 Oldest'],
    ];

    public function __construct(
        private ArtworkService $artworks,
        private ArtworkSearchService $search,
        private ContentTypeSlugResolver $contentTypeResolver,
        private ArtworkMaturityService $maturity,
    ) {
    }

    public function browse(Request $request)
    {
        $sort    = $this->resolveSort($request, 'trending');
        $perPage = $this->resolvePerPage($request);
        $page    = (int) $request->query('page', 1);
        $ttl     = self::SORT_TTL_MAP[$sort] ?? 300;

        $artworks = Cache::remember(
            "browse.all.catalog-visible.v2.{$sort}.{$page}",
            $ttl,
            fn () => $this->search->searchWithThumbnailPreference([
                'filter' => 'is_public = true AND is_approved = true',
                'sort'   => self::SORT_MAP[$sort] ?? ['created_at:desc'],
            ], $perPage, false, $page)
        );
        $artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
        $seo = $this->buildPaginationSeo($request, url('/browse'), $artworks);

        $mainCategories = $this->mainCategories();

        return view('gallery.index', [
            'gallery_type'    => 'browse',
            'mainCategories'  => $mainCategories,
            'subcategories'   => $mainCategories,
            'contentType'     => null,
            'category'        => null,
            'artworks'        => $artworks,
            'current_sort'    => $sort,
            'sort_options'    => self::SORT_OPTIONS,
            'hero_title'      => 'Browse Artworks',
            'hero_description' => 'List of all uploaded artworks across Skins, Wallpapers, Photography, and Other.',
            'breadcrumbs'     => collect([(object) ['name' => 'Explore', 'url' => '/browse']]),
            'page_title'      => 'Browse Uploaded Artworks - Photography, Wallpapers and Skins at SkinBase',
            'page_meta_description' => "Browse Uploaded Photography, Wallpapers and Skins to one of the world's oldest online social community for artists and art enthusiasts.",
            'page_meta_keywords'    => 'photography, wallpapers, skins, stock, browse, social, community, artist, picture, photo',
            'page_canonical'  => $seo['canonical'],
            'page_rel_prev'   => $seo['prev'],
            'page_rel_next'   => $seo['next'],
            'page_robots'     => 'index,follow',
        ]);
    }

    public function content(Request $request, string $contentTypeSlug, ?string $path = null)
    {
        $requestedSlug = strtolower($contentTypeSlug);
        $resolution = $this->contentTypeResolver->resolve($requestedSlug);

        if (! $resolution->found() || $resolution->contentType === null) {
            abort(404);
        }

        $contentType = $resolution->contentType;
        $contentSlug = strtolower((string) $contentType->slug);

        if ($resolution->requiresRedirect()) {
            return $this->redirectToContentTypePath($request, $contentSlug, $path, 301);
        }

        // Default sort: trending (not chronological)
        $sort    = $this->resolveSort($request, 'trending');
        $perPage = $this->resolvePerPage($request);
        $page    = (int) $request->query('page', 1);
        $ttl     = self::SORT_TTL_MAP[$sort] ?? 300;

        $mainCategories = $this->mainCategories();
        $rootCategories = $contentType->rootCategories()->orderBy('sort_order')->orderBy('name')->get();

        $normalizedPath = trim((string) $path, '/');
        if ($normalizedPath === '') {
            $artworks = Cache::remember(
                "gallery.ct.catalog-visible.v2.{$contentSlug}.{$sort}.{$page}",
                $ttl,
                fn () => $this->search->searchWithThumbnailPreference([
                    'filter' => 'is_public = true AND is_approved = true AND content_type = "' . $contentSlug . '"',
                    'sort'   => self::SORT_MAP[$sort] ?? ['created_at:desc'],
                ], $perPage, false, $page)
            );
            $artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
            $seo = $this->buildPaginationSeo($request, url('/' . $contentSlug), $artworks);

            return view('gallery.index', [
                'gallery_type'    => 'content-type',
                'mainCategories'  => $mainCategories,
                'subcategories'   => $rootCategories,
                'contentType'     => $contentType,
                'category'        => null,
                'artworks'        => $artworks,
                'current_sort'    => $sort,
                'sort_options'    => self::SORT_OPTIONS,
                'hero_title'      => $contentType->name,
                'hero_description' => $contentType->description ?? ($contentType->name . ' artworks on Skinbase.'),
                'breadcrumbs'     => collect([
                    (object) ['name' => 'Explore', 'url' => '/browse'],
                    (object) ['name' => $contentType->name, 'url' => '/' . $contentSlug],
                ]),
                'page_title'      => $contentType->name . ' – Skinbase Nova',
                'page_meta_description' => $contentType->description ?? ('Discover the best ' . $contentType->name . ' artworks on Skinbase'),
                'page_meta_keywords'    => strtolower($contentType->slug) . ', skinbase, artworks, wallpapers, skins, photography',
                'page_canonical'  => $seo['canonical'],
                'page_rel_prev'   => $seo['prev'],
                'page_rel_next'   => $seo['next'],
                'page_robots'     => 'index,follow',
            ]);
        }

        $segments = array_values(array_filter(explode('/', $normalizedPath)));
        $category = Category::findByPath($contentSlug, $segments);
        if (! $category) {
            abort(404);
        }

        $categorySlugs = $this->categoryFilterSlugs($category);
        $categoryFilter = collect($categorySlugs)
            ->map(fn (string $slug) => 'category = "' . addslashes($slug) . '"')
            ->implode(' OR ');

        $artworks = Cache::remember(
            'gallery.cat.catalog-visible.v2.' . md5($contentSlug . '|' . implode('|', $categorySlugs)) . ".{$sort}.{$page}",
            $ttl,
            fn () => $this->search->searchWithThumbnailPreference([
                'filter' => 'is_public = true AND is_approved = true AND (' . $categoryFilter . ')',
                'sort'   => self::SORT_MAP[$sort] ?? ['created_at:desc'],
            ], $perPage, false, $page)
        );
        $artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
        $seo = $this->buildPaginationSeo($request, url('/' . $contentSlug . '/' . strtolower($category->full_slug_path)), $artworks);

        $navigationCategory = $category->parent ?: $category;

        $subcategories = $navigationCategory->children()->orderBy('sort_order')->orderBy('name')->get();
        if ($subcategories->isEmpty()) {
            $subcategories = $rootCategories;
        }

        $breadcrumbs = collect(array_merge([
            (object) [
                'name' => 'Explore',
                'url' => '/browse',
            ],
            (object) [
                'name' => $contentType->name,
                'url' => '/' . $contentSlug,
            ],
        ], $category->breadcrumbs))
            ->map(function ($crumb) {
                return (object) [
                    'name' => $crumb->name,
                    'url' => $crumb->url,
                ];
            });

        return view('gallery.index', [
            'gallery_type'    => 'category',
            'mainCategories'  => $mainCategories,
            'subcategories'   => $subcategories,
            'subcategory_parent' => $navigationCategory,
            'contentType'     => $contentType,
            'category'        => $category,
            'artworks'        => $artworks,
            'current_sort'    => $sort,
            'sort_options'    => self::SORT_OPTIONS,
            'hero_title'      => $category->name,
            'hero_description' => $category->description ?? ($contentType->name . ' artworks on Skinbase.'),
            'breadcrumbs'     => $breadcrumbs,
            'page_title'      => $category->name . ' – Skinbase Nova',
            'page_meta_description' => $category->description ?? ('Discover the best ' . $category->name . ' ' . $contentType->name . ' artworks on Skinbase'),
            'page_meta_keywords'    => strtolower($contentType->slug) . ', skinbase, artworks, wallpapers, skins, photography',
            'page_canonical'  => $seo['canonical'],
            'page_rel_prev'   => $seo['prev'],
            'page_rel_next'   => $seo['next'],
            'page_robots'     => 'index,follow',
        ]);
    }

    public function showArtwork(...$params)
    {
        $req = request();
        $pathSegments = array_values(array_filter(explode('/', trim($req->path(), '/'))));

        $contentTypeSlug = $params[0] ?? ($pathSegments[0] ?? null);
        $categoryPath = $params[1] ?? null;
        $artwork = $params[2] ?? null;

        // If artwork wasn't provided (some route invocations supply fewer args),
        // derive it from the request path's last segment.
        if ($artwork === null) {
            $artwork = end($pathSegments) ?: null;
        }

        $contentTypeSlug = strtolower((string) $contentTypeSlug);
        $categoryPath = $categoryPath !== null ? trim((string) $categoryPath, '/') : (isset($pathSegments[1]) ? implode('/', array_slice($pathSegments, 1, max(0, count($pathSegments) - 2))) : '');

        $resolution = $this->contentTypeResolver->resolve($contentTypeSlug);
        if (! $resolution->found() || $resolution->contentType === null) {
            abort(404);
        }

        $resolvedContentTypeSlug = strtolower((string) $resolution->contentType->slug);

        // Normalize artwork param if route-model binding returned an Artwork model
        $artworkSlug = $artwork instanceof Artwork ? (string) $artwork->slug : (string) $artwork;

        if ($resolution->requiresRedirect()) {
            $path = trim($categoryPath . '/' . $artworkSlug, '/');

            return $this->redirectToContentTypePath($req, $resolvedContentTypeSlug, $path, 301);
        }

        return app(\App\Http\Controllers\ArtworkController::class)->show(
            $req,
            $resolvedContentTypeSlug,
            $categoryPath,
            $artworkSlug
        );
    }

    private function presentArtwork(Artwork $artwork): object
    {
        $primaryCategory = $artwork->categories->sortBy('sort_order')->first();
        $present         = ThumbnailPresenter::present($artwork, 'md');
        $group           = $artwork->group;
        $isGroupPublisher = $group !== null;
        $avatarUrl       = $isGroupPublisher
            ? $group->avatarUrl()
            : \App\Support\AvatarUrl::forUser(
                (int) ($artwork->user_id ?? 0),
                $artwork->user?->profile?->avatar_hash ?? null,
                64
            );
        $displayName     = $isGroupPublisher ? ($group->name ?? 'Skinbase') : ($artwork->user?->name ?? 'Skinbase');
        $username        = $isGroupPublisher ? '' : ($artwork->user?->username ?? '');
        $profileUrl      = $isGroupPublisher ? $group->publicUrl() : ($username !== '' ? '/@' . $username : null);

        return (object) $this->maturity->decoratePayload([
            'id'            => $artwork->id,
            'name'          => $artwork->title,
            'content_type_name' => $primaryCategory?->contentType?->name ?? '',
            'content_type_slug' => $primaryCategory?->contentType?->slug ?? '',
            'category_name' => $primaryCategory->name ?? '',
            'category_slug' => $primaryCategory->slug ?? '',
            'thumb_url'     => $present['url'],
            'thumb_srcset'  => $present['srcset'] ?? $present['url'],
            'uname'         => $displayName,
            'username'      => $username,
            'avatar_url'    => $avatarUrl,
            'profile_url'   => $profileUrl,
            'published_as_type' => $isGroupPublisher ? 'group' : 'user',
            'publisher'     => [
                'type' => $isGroupPublisher ? 'group' : 'user',
                'name' => $displayName,
                'username' => $username,
                'avatar_url' => $avatarUrl,
                'profile_url' => $profileUrl,
            ],
            'published_at'  => $artwork->published_at,
            'width'         => $artwork->width  ?? null,
            'height'        => $artwork->height ?? null,
        ], $artwork, request()->user());
    }

    /**
     * Build the category slug filter set for a gallery page.
     * Includes the current category and all descendant subcategories.
     *
     * @return array<int, string>
     */
    private function categoryFilterSlugs(Category $category): array
    {
        $category->loadMissing('descendants');

        $slugs = [];
        $stack = [$category];

        while ($stack !== []) {
            /** @var Category $current */
            $current = array_pop($stack);
            if (! empty($current->slug)) {
                $slugs[] = Str::lower($current->slug);
            }

            foreach ($current->children as $child) {
                $child->loadMissing('descendants');
                $stack[] = $child;
            }
        }

        return array_values(array_unique($slugs));
    }

    private function resolvePerPage(Request $request): int
    {
        $limit = (int) $request->query('limit', 0);
        $perPage = (int) $request->query('per_page', 0);

        // Spec §8: recommended 24 per page on category/gallery pages
        $value = $limit > 0 ? $limit : ($perPage > 0 ? $perPage : 24);

        return max(12, min($value, 80));
    }

    /**
     * Validate and return the requested sort alias, falling back to $default.
     * Only allows keys present in SORT_MAP.
     */
    private function resolveSort(Request $request, string $default = 'trending'): string
    {
        $requested = (string) $request->query('sort', $default);
        return array_key_exists($requested, self::SORT_MAP) ? $requested : $default;
    }

    private function mainCategories(): Collection
    {
        return $this->contentTypeResolver
            ->publicContentTypes()
            ->map(function (ContentType $type) {
                return (object) [
                    'id' => $type->id,
                    'name' => $type->name,
                    'slug' => $type->slug,
                    'url' => '/' . strtolower($type->slug),
                ];
            });
    }

    private function redirectToContentTypePath(Request $request, string $contentTypeSlug, ?string $path = null, int $status = 301): RedirectResponse
    {
        $target = url('/' . trim($contentTypeSlug . '/' . trim((string) $path, '/'), '/'));
        $queryString = $request->getQueryString();

        if ($queryString) {
            $target .= '?' . $queryString;
        }

        return redirect()->to($target, $status);
    }

    private function buildPaginationSeo(Request $request, string $canonicalBaseUrl, mixed $paginator): array
    {
        $canonicalQuery = $request->query();
        unset($canonicalQuery['grid']);
        if (($canonicalQuery['page'] ?? null) !== null && (int) $canonicalQuery['page'] <= 1) {
            unset($canonicalQuery['page']);
        }

        $canonical = $canonicalBaseUrl;
        if ($canonicalQuery !== []) {
            $canonical .= '?' . http_build_query($canonicalQuery);
        }

        $prev = null;
        $next = null;

        if ($paginator instanceof AbstractPaginator || $paginator instanceof AbstractCursorPaginator) {
            $prev = $this->stripQueryParamFromUrl($paginator->previousPageUrl(), 'grid');
            $next = $this->stripQueryParamFromUrl($paginator->nextPageUrl(), 'grid');
        }

        return [
            'canonical' => $canonical,
            'prev' => $prev,
            'next' => $next,
        ];
    }

    private function stripQueryParamFromUrl(?string $url, string $queryParam): ?string
    {
        if ($url === null || $url === '') {
            return null;
        }

        $parts = parse_url($url);
        if (!is_array($parts)) {
            return $url;
        }

        $query = [];
        if (!empty($parts['query'])) {
            parse_str($parts['query'], $query);
            unset($query[$queryParam]);
        }

        $rebuilt = '';
        if (isset($parts['scheme'])) {
            $rebuilt .= $parts['scheme'] . '://';
        }
        if (isset($parts['user'])) {
            $rebuilt .= $parts['user'];
            if (isset($parts['pass'])) {
                $rebuilt .= ':' . $parts['pass'];
            }
            $rebuilt .= '@';
        }
        if (isset($parts['host'])) {
            $rebuilt .= $parts['host'];
        }
        if (isset($parts['port'])) {
            $rebuilt .= ':' . $parts['port'];
        }
        $rebuilt .= $parts['path'] ?? '';

        if ($query !== []) {
            $rebuilt .= '?' . http_build_query($query);
        }

        if (isset($parts['fragment'])) {
            $rebuilt .= '#' . $parts['fragment'];
        }

        return $rebuilt;
    }
}
