<?php

declare(strict_types=1);

namespace App\Http\Controllers\Web;

use App\Events\NovaCards\NovaCardViewed;
use App\Http\Controllers\Controller;
use App\Jobs\UpdateNovaCardStatsJob;
use App\Models\NovaCard;
use App\Models\NovaCardCategory;
use App\Models\NovaCardChallenge;
use App\Models\NovaCardChallengeEntry;
use App\Models\NovaCardCollection;
use App\Models\NovaCardCreatorPreset;
use App\Models\NovaCardTag;
use App\Models\NovaCardTemplate;
use App\Models\User;
use App\Services\NovaCards\NovaCardPresenter;
use App\Services\NovaCards\NovaCardCommentService;
use App\Services\NovaCards\NovaCardLineageService;
use App\Services\NovaCards\NovaCardRelatedCardsService;
use App\Services\NovaCards\NovaCardRisingService;
use App\Support\UsernamePolicy;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class NovaCardsController extends Controller
{
    public function __construct(
        private readonly NovaCardPresenter $presenter,
        private readonly NovaCardCommentService $comments,
        private readonly NovaCardLineageService $lineage,
        private readonly NovaCardRisingService $risingService,
        private readonly NovaCardRelatedCardsService $relatedService,
    ) {}

    public function index(Request $request): View
    {
        $latest = $this->publicCardsQuery()->latest('published_at')->paginate(18)->withQueryString();
        $featured = $this->publicCardsQuery()->where('featured', true)->latest('published_at')->limit(6)->get();
        $trending = $this->publicCardsQuery()->orderByDesc('trending_score')->orderByDesc('last_engaged_at')->limit(6)->get();
        $rising = $this->risingService->risingCards(6);
        $collections = NovaCardCollection::query()
            ->with(['user', 'items.card.user.profile', 'items.card.category', 'items.card.template', 'items.card.backgroundImage', 'items.card.tags'])
            ->where(function ($query): void {
                $query->where('official', true)
                    ->orWhere('visibility', NovaCardCollection::VISIBILITY_PUBLIC);
            })
            ->orderByDesc('featured')
            ->orderByDesc('official')
            ->orderByDesc('updated_at')
            ->limit(4)
            ->get();
        $categories = NovaCardCategory::query()->where('active', true)->orderBy('order_num')->orderBy('name')->get();
        $tags = NovaCardTag::query()->withCount('cards')->orderByDesc('cards_count')->limit(12)->get();
        $styleFamilies = $this->styleFamilies();

        return view('cards.index', [
            'meta' => [
                'title' => 'Cards - Skinbase',
                'description' => 'Browse featured, trending, and latest Cards. Discover beautiful quote cards, mood cards, and visual text art by the Skinbase community.',
                'canonical' => route('cards.index'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Cards',
            'subheading' => (string) config('nova_cards.brand.subtitle'),
            'cards' => $this->presenter->cards($latest->items()),
            'pagination' => $latest,
            'featuredCards' => $this->presenter->cards($featured),
            'trendingCards' => $this->presenter->cards($trending),
            'risingCards' => $this->presenter->cards($rising->all()),
            'categories' => $categories,
            'tags' => $tags,
            'moodFamilies' => $this->moodFamilies(),
            'styleFamilies' => $styleFamilies,
            'paletteFamilies' => $this->paletteFamilies(),
            'seasonalHubs' => $this->seasonalHubs(),
            'collections' => $collections->map(fn (NovaCardCollection $collection): array => $this->presenter->collection($collection, $request->user()))->values()->all(),
            'context' => 'index',
        ]);
    }

    public function category(Request $request, string $categorySlug): View
    {
        $category = NovaCardCategory::query()->where('slug', $categorySlug)->where('active', true)->firstOrFail();
        $cards = $this->publicCardsQuery()->where('category_id', $category->id)->latest('published_at')->paginate(18)->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => $category->name . ' Cards - Skinbase',
                'description' => $category->description ?: ('Browse ' . strtolower($category->name) . ' Cards on Skinbase.'),
                'canonical' => route('cards.category', ['categorySlug' => $category->slug]),
                'robots' => 'index,follow',
            ],
            'heading' => $category->name,
            'subheading' => $category->description ?: 'Explore this Cards category.',
            'cards' => $this->presenter->cards($cards->items()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect([$category]),
            'tags' => collect(),
            'styleFamilies' => collect(),
            'context' => 'category',
        ]);
    }

    public function popular(Request $request): View
    {
        $cards = $this->publicCardsQuery()
            ->orderByDesc('trending_score')
            ->orderByDesc('likes_count')
            ->orderByDesc('saves_count')
            ->paginate(18)
            ->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => 'Popular Cards - Skinbase',
                'description' => 'Browse the most liked, saved, and viewed Cards on Skinbase.',
                'canonical' => route('cards.popular'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Popular cards',
            'subheading' => 'The cards earning the strongest saves, likes, and repeat views right now.',
            'cards' => $this->presenter->cards($cards->items(), false, $request->user()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'risingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'styleFamilies' => collect(),
            'context' => 'popular',
        ]);
    }

    public function rising(Request $request): View
    {
        $risingCollection = $this->risingService->risingCards(36);
        $page = max(1, (int) $request->query('page', 1));
        $perPage = 18;
        $paginated = new \Illuminate\Pagination\LengthAwarePaginator(
            $risingCollection->forPage($page, $perPage)->values(),
            $risingCollection->count(),
            $perPage,
            $page,
            ['path' => route('cards.rising')],
        );

        return view('cards.index', [
            'meta' => [
                'title' => 'Rising Cards - Skinbase',
                'description' => 'Discover Cards that are gaining traction right now — fresh creators and fast-rising saves and remixes.',
                'canonical' => route('cards.rising'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Rising',
            'subheading' => 'Fresh Cards gaining momentum right now.',
            'cards' => $this->presenter->cards($paginated->items(), false, $request->user()),
            'pagination' => $paginated,
            'featuredCards' => [],
            'trendingCards' => [],
            'risingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'styleFamilies' => collect(),
            'context' => 'rising',
        ]);
    }

    public function remixed(Request $request): View
    {
        $cards = $this->publicCardsQuery()
            ->whereNotNull('original_card_id')
            ->orderByDesc('published_at')
            ->paginate(18)
            ->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => 'Remixed Cards - Skinbase',
                'description' => 'Discover Cards remixed from community originals with attribution and lineage.',
                'canonical' => route('cards.remixed'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Remixed cards',
            'subheading' => 'Community reinterpretations linked back to their original Cards.',
            'cards' => $this->presenter->cards($cards->items(), false, $request->user()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'styleFamilies' => collect(),
            'paletteFamilies' => collect(),
            'context' => 'remixed',
        ]);
    }

    public function remixHighlights(Request $request): View
    {
        $cards = $this->publicCardsQuery()
            ->whereNotNull('original_card_id')
            ->orderByDesc('remixes_count')
            ->orderByDesc('saves_count')
            ->orderByDesc('likes_count')
            ->orderByDesc('published_at')
            ->paginate(18)
            ->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => 'Best Remixes - Skinbase',
                'description' => 'Browse standout Card remixes ranked by remix traction, saves, and likes.',
                'canonical' => route('cards.remix-highlights'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Best remixes',
            'subheading' => 'The strongest community reinterpretations ranked by remix traction and engagement.',
            'cards' => $this->presenter->cards($cards->items(), false, $request->user()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'styleFamilies' => collect(),
            'paletteFamilies' => collect(),
            'context' => 'remix-highlights',
        ]);
    }

    public function editorial(Request $request): View
    {
        $cards = NovaCard::query()
            ->with(['user.profile', 'category', 'template', 'backgroundImage', 'tags', 'originalCard', 'rootCard'])
            ->featuredEditorial()
            ->orderByDesc('featured_score')
            ->orderByDesc('featured')
            ->orderByDesc('published_at')
            ->paginate(18)
            ->withQueryString();

        $collections = NovaCardCollection::query()
            ->with(['user', 'items.card.user.profile', 'items.card.category', 'items.card.template', 'items.card.backgroundImage', 'items.card.tags'])
            ->where(function ($query): void {
                $query->where('featured', true)
                    ->orWhere('official', true);
            })
            ->orderByDesc('featured')
            ->orderByDesc('official')
            ->orderByDesc('updated_at')
            ->limit(4)
            ->get();

        $challenges = NovaCardChallenge::query()
            ->whereIn('status', [NovaCardChallenge::STATUS_ACTIVE, NovaCardChallenge::STATUS_COMPLETED])
            ->where(function ($query): void {
                $query->where('featured', true)
                    ->orWhere('official', true);
            })
            ->orderByDesc('featured')
            ->orderBy('starts_at')
            ->limit(4)
            ->get();
        $featuredCreators = User::query()
            ->where('nova_featured_creator', true)
            ->whereHas('novaCards', fn ($query) => $query->publiclyVisible())
            ->withCount([
                'novaCards as public_cards_count' => fn ($query) => $query->publiclyVisible(),
                'novaCards as featured_cards_count' => fn ($query) => $query->publiclyVisible()->where('featured', true),
            ])
            ->withSum([
                'novaCards as total_views_count' => fn ($query) => $query->publiclyVisible(),
            ], 'views_count')
            ->orderByDesc('featured_cards_count')
            ->orderByDesc('public_cards_count')
            ->orderBy('username')
            ->limit(6)
            ->get()
            ->map(fn (User $creator): array => [
                'id' => (int) $creator->id,
                'username' => (string) $creator->username,
                'display_name' => $creator->name ?: '@' . $creator->username,
                'public_url' => route('cards.creator', ['username' => $creator->username]),
                'public_cards_count' => (int) ($creator->public_cards_count ?? 0),
                'featured_cards_count' => (int) ($creator->featured_cards_count ?? 0),
                'total_views_count' => (int) ($creator->total_views_count ?? 0),
            ])
            ->values()
            ->all();

        return view('cards.index', [
            'meta' => [
                'title' => 'Editorial Picks - Cards - Skinbase',
                'description' => 'Browse editorial Cards picks, featured collections, and highlighted challenges.',
                'canonical' => route('cards.editorial'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Editorial picks',
            'subheading' => 'Curated Cards, featured collections, and standout challenge surfaces chosen for quality and cohesion.',
            'cards' => $this->presenter->cards($cards->items(), false, $request->user()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'moodFamilies' => collect(),
            'styleFamilies' => collect(),
            'paletteFamilies' => collect(),
            'seasonalHubs' => collect(),
            'featuredCreators' => $featuredCreators,
            'landingCollections' => $collections->map(fn (NovaCardCollection $collection): array => $this->presenter->collection($collection, $request->user()))->values()->all(),
            'landingChallenges' => $challenges,
            'context' => 'editorial',
        ]);
    }

    public function seasonal(Request $request): View
    {
        $seasonalHubs = $this->seasonalHubs();
        $cards = $this->cardsTaggedWithSlugs($seasonalHubs->pluck('tag_slugs')->flatten()->unique()->values()->all())
            ->latest('published_at')
            ->paginate(18)
            ->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => 'Seasonal Cards - Cards - Skinbase',
                'description' => 'Browse seasonal and event-aware Cards grouped by recurring moods, holidays, and time-of-year themes.',
                'canonical' => route('cards.seasonal'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Seasonal cards',
            'subheading' => 'Discover Cards grouped by recurring seasonal and campaign-style themes.',
            'cards' => $this->presenter->cards($cards->items(), false, $request->user()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'moodFamilies' => collect(),
            'styleFamilies' => collect(),
            'paletteFamilies' => collect(),
            'seasonalHubs' => $seasonalHubs,
            'landingCollections' => [],
            'landingChallenges' => collect(),
            'context' => 'seasonal',
        ]);
    }

    public function challenges(Request $request): View
    {
        $challenges = NovaCardChallenge::query()
            ->with(['winnerCard.user'])
            ->whereIn('status', [NovaCardChallenge::STATUS_ACTIVE, NovaCardChallenge::STATUS_COMPLETED])
            ->orderByDesc('featured')
            ->orderBy('starts_at')
            ->get();

        return view('cards.challenges', [
            'meta' => [
                'title' => 'Card Challenges - Skinbase',
                'description' => 'Browse active and completed Cards challenges, prompts, and winners.',
                'canonical' => route('cards.challenges'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Card challenges',
            'subheading' => 'Official prompts and community challenge runs for Cards creators.',
            'challenges' => $challenges,
        ]);
    }

    public function challenge(Request $request, string $slug): View
    {
        $challenge = NovaCardChallenge::query()
            ->with(['winnerCard.user', 'entries.card.user'])
            ->where('slug', $slug)
            ->firstOrFail();

        $entries = $challenge->entries
            ->filter(fn ($entry): bool => $entry->card !== null && $entry->card->canBeViewedBy($request->user()))
            ->sortByDesc(fn ($entry) => $entry->created_at)
            ->values();

        return view('cards.challenges', [
            'meta' => [
                'title' => $challenge->title . ' - Skinbase',
                'description' => $challenge->description ?: 'Browse entries for this Cards challenge.',
                'canonical' => route('cards.challenges.show', ['slug' => $challenge->slug]),
                'robots' => 'index,follow',
            ],
            'heading' => $challenge->title,
            'subheading' => $challenge->description ?: 'Challenge entries and winner picks.',
            'challenge' => $challenge,
            'challengeEntryItems' => $entries->map(fn ($entry): array => [
                'id' => (int) $entry->id,
                'status' => (string) $entry->status,
                'note' => $entry->note,
                'card' => $this->presenter->card($entry->card, false, $request->user()),
            ])->values()->all(),
            'challenges' => collect([$challenge]),
        ]);
    }

    public function templates(Request $request): View
    {
        return view('cards.resources', [
            'meta' => [
                'title' => 'Template Packs - Skinbase',
                'description' => 'Browse official Cards template packs and starting points.',
                'canonical' => route('cards.templates'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Template packs',
            'subheading' => 'Official starter packs for editorial, story, and community remix workflows.',
            'packs' => collect($this->presenter->options()['template_packs'] ?? []),
            'templates' => collect($this->presenter->options()['templates'] ?? []),
            'resourceType' => 'template',
        ]);
    }

    public function assets(Request $request): View
    {
        return view('cards.resources', [
            'meta' => [
                'title' => 'Asset Packs - Skinbase',
                'description' => 'Browse official Cards asset packs for decorative and editorial layouts.',
                'canonical' => route('cards.assets'),
                'robots' => 'index,follow',
            ],
            'heading' => 'Asset packs',
            'subheading' => 'Official decorative and editorial pack sets for the Cards v2 editor.',
            'packs' => collect($this->presenter->options()['asset_packs'] ?? []),
            'templates' => collect(),
            'resourceType' => 'asset',
        ]);
    }

    public function tag(Request $request, string $tagSlug): View
    {
        $tag = NovaCardTag::query()->where('slug', $tagSlug)->firstOrFail();
        $cards = $this->publicCardsQuery()->whereHas('tags', fn ($query) => $query->where('nova_card_tags.id', $tag->id))->latest('published_at')->paginate(18)->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => '#' . $tag->name . ' Cards - Skinbase',
                'description' => 'Browse Cards tagged with #' . $tag->name . ' on Skinbase.',
                'canonical' => route('cards.tag', ['tagSlug' => $tag->slug]),
                'robots' => 'index,follow',
            ],
            'heading' => '#' . $tag->name,
            'subheading' => 'Browse cards using this tag.',
            'cards' => $this->presenter->cards($cards->items()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect([$tag]),
            'moodFamilies' => collect(),
            'styleFamilies' => collect(),
            'paletteFamilies' => collect(),
            'seasonalHubs' => collect(),
            'context' => 'tag',
        ]);
    }

    public function mood(Request $request, string $moodSlug): View
    {
        $mood = $this->moodFamilies()->firstWhere('key', $moodSlug);
        abort_unless($mood !== null, 404);

        $cards = $this->cardsTaggedWithSlugs((array) ($mood['tag_slugs'] ?? []))
            ->latest('published_at')
            ->paginate(18)
            ->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => $mood['label'] . ' Mood Cards - Skinbase',
                'description' => 'Browse Cards grouped into the ' . strtolower((string) $mood['label']) . ' mood family on Skinbase.',
                'canonical' => route('cards.mood', ['moodSlug' => $mood['key']]),
                'robots' => 'index,follow',
            ],
            'heading' => $mood['label'],
            'subheading' => 'Discover Cards grouped by a curated mood family using durable tag mappings.',
            'cards' => $this->presenter->cards($cards->items(), false, $request->user()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'moodFamilies' => $this->moodFamilies(),
            'styleFamilies' => collect(),
            'paletteFamilies' => collect(),
            'seasonalHubs' => collect(),
            'context' => 'mood',
        ]);
    }

    public function style(Request $request, string $styleSlug): View
    {
        $style = $this->styleFamilies()->firstWhere('key', $styleSlug);
        abort_unless($style !== null, 404);

        $cards = $this->publicCardsQuery()
            ->where('style_family', $style['key'])
            ->latest('published_at')
            ->paginate(18)
            ->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => $style['label'] . ' Style Cards - Skinbase',
                'description' => 'Browse Cards using the ' . strtolower((string) $style['label']) . ' style family on Skinbase.',
                'canonical' => route('cards.style', ['styleSlug' => $style['key']]),
                'robots' => 'index,follow',
            ],
            'heading' => $style['label'],
            'subheading' => 'Discover Cards grouped by a shared visual style family.',
            'cards' => $this->presenter->cards($cards->items(), false, $request->user()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'moodFamilies' => collect(),
            'styleFamilies' => $this->styleFamilies(),
            'paletteFamilies' => $this->paletteFamilies(),
            'seasonalHubs' => collect(),
            'context' => 'style',
        ]);
    }

    public function palette(Request $request, string $paletteSlug): View
    {
        $palette = $this->paletteFamilies()->firstWhere('key', $paletteSlug);
        abort_unless($palette !== null, 404);

        $cards = $this->publicCardsQuery()
            ->where('palette_family', $palette['key'])
            ->latest('published_at')
            ->paginate(18)
            ->withQueryString();

        return view('cards.index', [
            'meta' => [
                'title' => $palette['label'] . ' Palette Cards - Skinbase',
                'description' => 'Browse Cards using the ' . strtolower((string) $palette['label']) . ' palette family on Skinbase.',
                'canonical' => route('cards.palette', ['paletteSlug' => $palette['key']]),
                'robots' => 'index,follow',
            ],
            'heading' => $palette['label'],
            'subheading' => 'Discover Cards grouped by shared palette families and color direction.',
            'cards' => $this->presenter->cards($cards->items(), false, $request->user()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'moodFamilies' => collect(),
            'styleFamilies' => $this->styleFamilies(),
            'paletteFamilies' => $this->paletteFamilies(),
            'seasonalHubs' => collect(),
            'context' => 'palette',
        ]);
    }

    public function creator(Request $request, string $username): View|RedirectResponse
    {
        $normalized = UsernamePolicy::normalize($username);
        $user = User::query()->whereRaw('LOWER(username) = ?', [$normalized])->firstOrFail();

        if ($username !== strtolower((string) $user->username)) {
            return redirect()->route('cards.creator', ['username' => strtolower((string) $user->username)], 301);
        }

        return view('cards.index', array_merge($this->creatorPagePayload($request, $user), [
            'meta' => [
                'title' => '@' . $user->username . ' Cards - Skinbase',
                'description' => 'Browse Cards created by @' . $user->username . ' on Skinbase.',
                'canonical' => route('cards.creator', ['username' => strtolower((string) $user->username)]),
                'robots' => 'index,follow',
            ],
            'heading' => '@' . $user->username,
            'subheading' => 'Cards created by ' . ($user->name ?: '@' . $user->username),
            'context' => 'creator',
        ]));
    }

    public function creatorPortfolio(Request $request, string $username): View|RedirectResponse
    {
        $normalized = UsernamePolicy::normalize($username);
        $user = User::query()->whereRaw('LOWER(username) = ?', [$normalized])->firstOrFail();

        if ($username !== strtolower((string) $user->username)) {
            return redirect()->route('cards.creator.portfolio', ['username' => strtolower((string) $user->username)], 301);
        }

        return view('cards.index', array_merge($this->creatorPagePayload($request, $user), [
            'meta' => [
                'title' => '@' . $user->username . ' Portfolio - Skinbase',
                'description' => 'Browse the dedicated Cards portfolio page for @' . $user->username . ' on Skinbase.',
                'canonical' => route('cards.creator.portfolio', ['username' => strtolower((string) $user->username)]),
                'robots' => 'index,follow',
            ],
            'heading' => '@' . $user->username . ' Portfolio',
            'subheading' => 'A dedicated Cards portfolio view for ' . ($user->name ?: '@' . $user->username) . '.',
            'context' => 'creator-portfolio',
        ]));
    }

    private function creatorPagePayload(Request $request, User $user): array
    {

        $creatorCards = $this->publicCardsQuery()->where('user_id', $user->id);
        $cards = (clone $creatorCards)->latest('published_at')->paginate(18)->withQueryString();
        $creatorSummary = $this->buildCreatorSummary($user);
        $creatorFeaturedWorks = $this->presenter->cards($this->creatorFeaturedWorks($user)->all(), false, $request->user());
        $creatorFeaturedCollections = NovaCardCollection::query()
            ->with(['user', 'items.card.user.profile', 'items.card.category', 'items.card.template', 'items.card.backgroundImage', 'items.card.tags'])
            ->where('user_id', $user->id)
            ->where('featured', true)
            ->where(function ($query): void {
                $query->where('official', true)
                    ->orWhere('visibility', NovaCardCollection::VISIBILITY_PUBLIC);
            })
            ->orderByDesc('official')
            ->orderByDesc('updated_at')
            ->limit(3)
            ->get()
            ->map(fn (NovaCardCollection $collection): array => $this->presenter->collection($collection, $request->user()))
            ->values()
            ->all();
        $creatorHighlights = $this->presenter->cards(
            $this->creatorHighlights($user, $creatorFeaturedWorks !== [])->all(),
            false,
            $request->user()
        );
        $creatorMostRemixedWorks = $this->presenter->cards(
            $this->creatorMostRemixedWorks($user)->all(),
            false,
            $request->user()
        );
        $creatorMostLikedWorks = $this->presenter->cards(
            $this->creatorMostLikedWorks($user)->all(),
            false,
            $request->user()
        );
        $creatorRemixActivity = $this->creatorRemixActivity($user, $request->user());
        $creatorRemixGraph = $this->creatorRemixGraph($user);
        $creatorPreferenceSignals = $this->creatorPreferenceSignals($user);
        $creatorTimeline = $this->creatorTimeline($user, $request->user());
        $creatorChallengeHistory = $this->creatorChallengeHistory($user);

        return [
            'cards' => $this->presenter->cards($cards->items()),
            'pagination' => $cards,
            'featuredCards' => [],
            'trendingCards' => [],
            'categories' => collect(),
            'tags' => collect(),
            'moodFamilies' => collect(),
            'styleFamilies' => collect(),
            'paletteFamilies' => collect(),
            'seasonalHubs' => collect(),
            'creatorSummary' => $creatorSummary,
            'creatorFeaturedWorks' => $creatorFeaturedWorks,
            'creatorFeaturedCollections' => $creatorFeaturedCollections,
            'creatorHighlights' => $creatorHighlights,
            'creatorMostRemixedWorks' => $creatorMostRemixedWorks,
            'creatorMostLikedWorks' => $creatorMostLikedWorks,
            'creatorRemixActivity' => $creatorRemixActivity,
            'creatorRemixGraph' => $creatorRemixGraph,
            'creatorPreferenceSignals' => $creatorPreferenceSignals,
            'creatorTimeline' => $creatorTimeline,
            'creatorChallengeHistory' => $creatorChallengeHistory,
        ];
    }

    public function collection(Request $request, string $slug, int $id): View|RedirectResponse
    {
        $collection = NovaCardCollection::query()
            ->with(['user', 'items.card.user.profile', 'items.card.category', 'items.card.template', 'items.card.backgroundImage', 'items.card.tags'])
            ->findOrFail($id);

        abort_unless($collection->isPubliclyVisible(), 404);

        if ($slug !== $collection->slug) {
            return redirect()->route('cards.collections.show', ['slug' => $collection->slug, 'id' => $collection->id], 301);
        }

        return view('cards.collection', [
            'meta' => [
                'title' => $collection->name . ' - Cards Collection - Skinbase',
                'description' => $collection->description ?: 'Browse this curated Cards collection.',
                'canonical' => route('cards.collections.show', ['slug' => $collection->slug, 'id' => $collection->id]),
                'robots' => 'index,follow',
            ],
            'collection' => $this->presenter->collection($collection, $request->user(), true),
        ]);
    }

    public function lineage(Request $request, string $slug, int $id): View|RedirectResponse
    {
        $card = NovaCard::query()
            ->with(['user.profile', 'category', 'template', 'backgroundImage', 'tags', 'originalCard.user', 'rootCard.user'])
            ->published()
            ->findOrFail($id);

        abort_unless($card->canBeViewedBy($request->user()), 404);

        if ($slug !== $card->slug) {
            return redirect()->route('cards.lineage', ['slug' => $card->slug, 'id' => $card->id], 301);
        }

        $lineage = $this->lineage->resolve($card, $request->user());

        return view('cards.lineage', [
            'meta' => [
                'title' => $card->title . ' Lineage - Cards - Skinbase',
                'description' => 'Browse the remix lineage and related variants for this Nova Card.',
                'canonical' => route('cards.lineage', ['slug' => $card->slug, 'id' => $card->id]),
                'robots' => 'index,follow',
            ],
            'card' => $lineage['card'],
            'trail' => $lineage['trail'],
            'rootCard' => $lineage['root_card'],
            'familyCards' => $lineage['family_cards'],
        ]);
    }

    public function show(Request $request, string $slug, int $id): View|RedirectResponse
    {
        $card = NovaCard::query()
            ->with(['user.profile', 'category', 'template', 'backgroundImage', 'tags'])
            ->published()
            ->findOrFail($id);

        if (! $card->canBeViewedBy($request->user())) {
            abort(404);
        }

        if ($slug !== $card->slug) {
            return redirect()->route('cards.show', ['slug' => $card->slug, 'id' => $card->id], 301);
        }

        $card->increment('views_count');
        $card->refresh();
        UpdateNovaCardStatsJob::dispatch($card->id);
        event(new NovaCardViewed($card, $request->user()?->id));

        $relatedByCategory = $card->category_id
            ? $this->publicCardsQuery()->where('category_id', $card->category_id)->where('id', '!=', $card->id)->limit(6)->get()
            : collect();
        $relatedByTags = $card->tags->isNotEmpty()
            ? $this->publicCardsQuery()->where('id', '!=', $card->id)->whereHas('tags', fn ($query) => $query->whereIn('nova_card_tags.id', $card->tags->pluck('id')))->limit(6)->get()
            : collect();
        $moreFromCreator = $this->publicCardsQuery()->where('user_id', $card->user_id)->where('id', '!=', $card->id)->limit(6)->get();

        // v3: smart related cards using relatedness service.
        $smartRelated = $this->relatedService->related($card, 8);

        return view('cards.show', [
            'card' => $this->presenter->card($card, true, $request->user()),
            'meta' => [
                'title' => $card->title . ' - Cards - Skinbase',
                'description' => $card->description ?: $card->quote_text,
                'canonical' => route('cards.show', ['slug' => $card->slug, 'id' => $card->id]),
                'robots' => $card->visibility === NovaCard::VISIBILITY_PUBLIC ? 'index,follow' : 'noindex,follow',
            ],
            'relatedByCategory' => $this->presenter->cards($relatedByCategory),
            'relatedByTags' => $this->presenter->cards($relatedByTags),
            'moreFromCreator' => $this->presenter->cards($moreFromCreator),
            'smartRelated' => $this->presenter->cards($smartRelated->all()),
            'challengeEntries' => $card->challengeEntries()
                ->with('challenge')
                ->whereNotIn('status', [\App\Models\NovaCardChallengeEntry::STATUS_HIDDEN, \App\Models\NovaCardChallengeEntry::STATUS_REJECTED])
                ->latest()
                ->limit(4)
                ->get(),
            'comments' => $this->comments->mapComments($card, $request->user()),
        ]);
    }

    private function publicCardsQuery()
    {
        return NovaCard::query()
            ->with(['user.profile', 'category', 'template', 'backgroundImage', 'tags', 'originalCard', 'rootCard'])
            ->publiclyVisible();
    }

    private function buildCreatorSummary(User $user): array
    {
        $aggregate = $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->selectRaw('COUNT(*) as total_cards')
            ->selectRaw('SUM(CASE WHEN featured = 1 THEN 1 ELSE 0 END) as total_featured_cards')
            ->selectRaw('COALESCE(SUM(views_count), 0) as total_views')
            ->selectRaw('COALESCE(SUM(likes_count), 0) as total_likes')
            ->selectRaw('COALESCE(SUM(saves_count), 0) as total_saves')
            ->selectRaw('COALESCE(SUM(remixes_count), 0) as total_remixes')
            ->selectRaw('COALESCE(SUM(challenge_entries_count), 0) as total_challenge_entries')
            ->first();

        $topStyles = $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->whereNotNull('style_family')
            ->select('style_family')
            ->selectRaw('COUNT(*) as cards_count')
            ->groupBy('style_family')
            ->orderByDesc('cards_count')
            ->limit(4)
            ->get()
            ->map(fn (NovaCard $card): array => [
                'key' => (string) $card->style_family,
                'label' => str($card->style_family)->replace('-', ' ')->title()->toString(),
                'cards_count' => (int) $card->cards_count,
            ])
            ->values()
            ->all();

        $topCategories = NovaCardCategory::query()
            ->whereHas('cards', fn ($query) => $query->publiclyVisible()->where('user_id', $user->id))
            ->withCount(['cards as creator_cards_count' => fn ($query) => $query->publiclyVisible()->where('user_id', $user->id)])
            ->orderByDesc('creator_cards_count')
            ->orderBy('name')
            ->limit(4)
            ->get()
            ->map(fn (NovaCardCategory $category): array => [
                'slug' => (string) $category->slug,
                'name' => (string) $category->name,
                'cards_count' => (int) $category->creator_cards_count,
            ])
            ->values()
            ->all();

        $tagCounts = NovaCardTag::query()
            ->select('nova_card_tags.id', 'nova_card_tags.slug', 'nova_card_tags.name')
            ->join('nova_card_tag_relation', 'nova_card_tag_relation.tag_id', '=', 'nova_card_tags.id')
            ->join('nova_cards', 'nova_cards.id', '=', 'nova_card_tag_relation.card_id')
            ->where('nova_cards.user_id', $user->id)
            ->where('nova_cards.status', NovaCard::STATUS_PUBLISHED)
            ->where('nova_cards.visibility', NovaCard::VISIBILITY_PUBLIC)
            ->whereNotIn('nova_cards.moderation_status', [NovaCard::MOD_FLAGGED, NovaCard::MOD_REJECTED])
            ->selectRaw('COUNT(*) as cards_count')
            ->groupBy('nova_card_tags.id', 'nova_card_tags.slug', 'nova_card_tags.name')
            ->orderByDesc(DB::raw('COUNT(*)'))
            ->orderBy('nova_card_tags.name')
            ->get();

        $topTags = $tagCounts
            ->take(6)
            ->map(fn (NovaCardTag $tag): array => [
                'slug' => (string) $tag->slug,
                'name' => (string) $tag->name,
                'cards_count' => (int) $tag->cards_count,
            ])
            ->values()
            ->all();

        $topPalettes = $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->whereNotNull('palette_family')
            ->select('palette_family')
            ->selectRaw('COUNT(*) as cards_count')
            ->groupBy('palette_family')
            ->orderByDesc('cards_count')
            ->limit(4)
            ->get()
            ->map(fn (NovaCard $card): array => [
                'key' => (string) $card->palette_family,
                'label' => str($card->palette_family)->replace('-', ' ')->title()->toString(),
                'cards_count' => (int) $card->cards_count,
            ])
            ->values()
            ->all();

        $topMoods = $this->moodFamilies()
            ->map(function (array $mood) use ($tagCounts): array {
                $count = $tagCounts
                    ->filter(fn (NovaCardTag $tag): bool => in_array((string) $tag->slug, $mood['tag_slugs'], true))
                    ->sum(fn (NovaCardTag $tag): int => (int) $tag->cards_count);

                return [
                    'key' => $mood['key'],
                    'label' => $mood['label'],
                    'cards_count' => $count,
                ];
            })
            ->filter(fn (array $mood): bool => (int) $mood['cards_count'] > 0)
            ->sortByDesc('cards_count')
            ->take(4)
            ->values()
            ->all();

        return [
            'creator' => [
                'username' => (string) $user->username,
                'name' => $user->name,
                'display_name' => $user->name ?: '@' . $user->username,
            ],
            'stats' => [
                'total_cards' => (int) ($aggregate->total_cards ?? 0),
                'total_featured_cards' => (int) ($aggregate->total_featured_cards ?? 0),
                'total_views' => (int) ($aggregate->total_views ?? 0),
                'total_likes' => (int) ($aggregate->total_likes ?? 0),
                'total_saves' => (int) ($aggregate->total_saves ?? 0),
                'total_remixes' => (int) ($aggregate->total_remixes ?? 0),
                'total_challenge_entries' => (int) ($aggregate->total_challenge_entries ?? 0),
            ],
            'top_styles' => $topStyles,
            'top_palettes' => $topPalettes,
            'top_moods' => $topMoods,
            'top_categories' => $topCategories,
            'top_tags' => $topTags,
        ];
    }

    private function creatorFeaturedWorks(User $user)
    {
        return $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->where('featured', true)
            ->orderByDesc('featured_score')
            ->orderByDesc('published_at')
            ->limit(4)
            ->get();
    }

    private function creatorHighlights(User $user, bool $excludeFeatured = false)
    {
        $query = $this->publicCardsQuery()
            ->where('user_id', $user->id);

        if ($excludeFeatured) {
            $query->where('featured', false);
        }

        $query
            ->orderByDesc('featured')
            ->orderByDesc('featured_score')
            ->orderByDesc('saves_count')
            ->orderByDesc('likes_count')
            ->orderByDesc('views_count')
            ->limit(4);

        return $query->get();
    }

    private function creatorMostRemixedWorks(User $user)
    {
        return $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->where('remixes_count', '>', 0)
            ->orderByDesc('remixes_count')
            ->orderByDesc('saves_count')
            ->orderByDesc('likes_count')
            ->orderByDesc('published_at')
            ->limit(4)
            ->get();
    }

    private function creatorMostLikedWorks(User $user)
    {
        return $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->where(function ($query): void {
                $query->where('likes_count', '>', 0)
                    ->orWhere('saves_count', '>', 0);
            })
            ->orderByDesc('likes_count')
            ->orderByDesc('saves_count')
            ->orderByDesc('views_count')
            ->orderByDesc('published_at')
            ->limit(4)
            ->get();
    }

    private function creatorRemixActivity(User $user, ?User $viewer = null): array
    {
        $cards = $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->where(function ($query): void {
                $query->where('remixes_count', '>', 0)
                    ->orWhereNotNull('original_card_id');
            })
            ->orderByDesc('remixes_count')
            ->orderByDesc('published_at')
            ->limit(6)
            ->get();

        $branchCards = $cards
            ->map(function (NovaCard $card) use ($viewer): array {
                $presented = $this->presenter->card($card, false, $viewer);
                $branchType = $card->original_card_id ? 'Published remix' : 'Community branch';
                $sourceLabel = $card->originalCard?->title ?? $card->rootCard?->title ?? $card->title;

                return [
                    'card' => $presented,
                    'branch_type' => $branchType,
                    'source_label' => $sourceLabel,
                    'lineage_url' => route('cards.lineage', ['slug' => $card->slug, 'id' => $card->id]),
                ];
            })
            ->values()
            ->all();

        return [
            'total_cards_remixed_by_community' => $cards->whereNull('original_card_id')->filter(fn (NovaCard $card): bool => (int) $card->remixes_count > 0)->count(),
            'total_published_remixes' => $cards->whereNotNull('original_card_id')->count(),
            'branches' => $branchCards,
        ];
    }

    private function creatorRemixGraph(User $user): array
    {
        $cards = $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->where(function ($query): void {
                $query->where('remixes_count', '>', 0)
                    ->orWhereNotNull('original_card_id')
                    ->orWhereNotNull('root_card_id');
            })
            ->get();

        $branches = $cards
            ->groupBy(fn (NovaCard $card): string => (string) ($card->root_card_id ?: $card->id))
            ->map(function ($group): array {
                /** @var NovaCard $root */
                $root = $group->firstWhere('id', $group->first()->root_card_id ?: $group->first()->id) ?? $group->first();
                $peak = $group->sortByDesc('remixes_count')->first();

                return [
                    'root_title' => (string) $root->title,
                    'cards_count' => $group->count(),
                    'total_remixes' => (int) $group->sum('remixes_count'),
                    'peak_title' => (string) ($peak?->title ?? $root->title),
                ];
            })
            ->sortByDesc('total_remixes')
            ->take(4)
            ->values();

        $maxRemixes = max(1, (int) $branches->max('total_remixes'));

        return $branches
            ->map(fn (array $branch): array => [
                ...$branch,
                'width_percent' => (int) max(16, round(($branch['total_remixes'] / $maxRemixes) * 100)),
            ])
            ->all();
    }

    private function creatorPreferenceSignals(User $user): array
    {
        $topFormats = $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->select('format')
            ->selectRaw('COUNT(*) as cards_count')
            ->groupBy('format')
            ->orderByDesc('cards_count')
            ->limit(3)
            ->get()
            ->map(fn (NovaCard $card): array => [
                'key' => (string) $card->format,
                'label' => str($card->format)->replace('-', ' ')->title()->toString(),
                'cards_count' => (int) $card->cards_count,
            ])
            ->values()
            ->all();

        $topTemplates = NovaCardTemplate::query()
            ->whereHas('cards', fn ($query) => $query->publiclyVisible()->where('user_id', $user->id))
            ->withCount(['cards as creator_cards_count' => fn ($query) => $query->publiclyVisible()->where('user_id', $user->id)])
            ->orderByDesc('creator_cards_count')
            ->orderBy('name')
            ->limit(3)
            ->get()
            ->map(fn (NovaCardTemplate $template): array => [
                'name' => (string) $template->name,
                'cards_count' => (int) $template->creator_cards_count,
            ])
            ->values()
            ->all();

        $editorModes = $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->whereNotNull('editor_mode_last_used')
            ->select('editor_mode_last_used')
            ->selectRaw('COUNT(*) as cards_count')
            ->groupBy('editor_mode_last_used')
            ->orderByDesc('cards_count')
            ->get();

        $presetCounts = NovaCardCreatorPreset::query()
            ->where('user_id', $user->id)
            ->select('preset_type')
            ->selectRaw('COUNT(*) as presets_count')
            ->groupBy('preset_type')
            ->orderByDesc('presets_count')
            ->get()
            ->map(fn (NovaCardCreatorPreset $preset): array => [
                'type' => (string) $preset->preset_type,
                'label' => str($preset->preset_type)->replace('-', ' ')->title()->toString(),
                'presets_count' => (int) $preset->presets_count,
            ])
            ->values()
            ->all();

        return [
            'top_formats' => $topFormats,
            'top_templates' => $topTemplates,
            'preferred_editor_mode' => $editorModes->isNotEmpty()
                ? [
                    'key' => (string) $editorModes->first()->editor_mode_last_used,
                    'label' => (string) str((string) $editorModes->first()->editor_mode_last_used)->replace('-', ' ')->title(),
                    'cards_count' => (int) $editorModes->first()->cards_count,
                ]
                : null,
            'preset_counts' => $presetCounts,
        ];
    }

    private function creatorTimeline(User $user, ?User $viewer = null): array
    {
        return $this->publicCardsQuery()
            ->where('user_id', $user->id)
            ->latest('published_at')
            ->latest('id')
            ->limit(6)
            ->get()
            ->map(function (NovaCard $card) use ($viewer): array {
                $presented = $this->presenter->card($card, false, $viewer);
                $signals = [];

                if ($card->featured) {
                    $signals[] = 'Featured release';
                }

                if ((int) $card->remixes_count > 0) {
                    $signals[] = 'Remix traction';
                }

                if ((int) $card->likes_count > 0 || (int) $card->saves_count > 0) {
                    $signals[] = 'Audience favorite';
                }

                return [
                    'card' => $presented,
                    'signals' => $signals,
                ];
            })
            ->values()
            ->all();
    }

    private function creatorChallengeHistory(User $user): array
    {
        return NovaCardChallengeEntry::query()
            ->with(['challenge', 'card.user'])
            ->where('user_id', $user->id)
            ->whereNotIn('status', [NovaCardChallengeEntry::STATUS_HIDDEN, NovaCardChallengeEntry::STATUS_REJECTED])
            ->whereHas('card', fn ($query) => $query->publiclyVisible()->where('user_id', $user->id))
            ->whereHas('challenge')
            ->orderByRaw("CASE status WHEN 'winner' THEN 0 WHEN 'featured' THEN 1 WHEN 'submitted' THEN 2 WHEN 'active' THEN 3 ELSE 4 END")
            ->latest('id')
            ->limit(4)
            ->get()
            ->map(fn (NovaCardChallengeEntry $entry): array => [
                'status_label' => match ((string) $entry->status) {
                    NovaCardChallengeEntry::STATUS_WINNER => 'Winner entry',
                    NovaCardChallengeEntry::STATUS_FEATURED => 'Featured entry',
                    NovaCardChallengeEntry::STATUS_ACTIVE => 'Active entry',
                    default => 'Submitted entry',
                },
                'challenge_title' => (string) ($entry->challenge?->title ?? 'Challenge'),
                'challenge_url' => $entry->challenge ? route('cards.challenges.show', ['slug' => $entry->challenge->slug]) : null,
                'challenge_status' => (string) ($entry->challenge?->status ?? ''),
                'card_title' => (string) ($entry->card?->title ?? 'Card'),
                'card_url' => $entry->card ? $entry->card->publicUrl() : null,
                'official' => (bool) ($entry->challenge?->official ?? false),
            ])
            ->values()
            ->all();
    }

    private function cardsTaggedWithSlugs(array $tagSlugs)
    {
        $tagSlugs = collect($tagSlugs)->filter()->unique()->values()->all();

        return $this->publicCardsQuery()->whereHas('tags', fn ($query) => $query->whereIn('nova_card_tags.slug', $tagSlugs));
    }

    private function moodFamilies()
    {
        return collect((array) config('nova_cards.mood_families', []))
            ->map(fn (array $mood): array => [
                'key' => (string) ($mood['key'] ?? ''),
                'label' => (string) ($mood['label'] ?? str((string) ($mood['key'] ?? ''))->replace('-', ' ')->title()),
                'tag_slugs' => array_values((array) ($mood['tag_slugs'] ?? [])),
            ])
            ->filter(fn (array $mood): bool => $mood['key'] !== '')
            ->values();
    }

    private function styleFamilies()
    {
        return collect((array) config('nova_cards.style_families', []))
            ->map(fn (array $style): array => [
                'key' => (string) ($style['key'] ?? ''),
                'label' => (string) ($style['label'] ?? str((string) ($style['key'] ?? ''))->replace('-', ' ')->title()),
            ])
            ->filter(fn (array $style): bool => $style['key'] !== '')
            ->values();
    }

    private function paletteFamilies()
    {
        return collect((array) config('nova_cards.palette_families', []))
            ->map(fn (array $palette): array => [
                'key' => (string) ($palette['key'] ?? ''),
                'label' => (string) ($palette['label'] ?? str((string) ($palette['key'] ?? ''))->replace('-', ' ')->title()),
            ])
            ->filter(fn (array $palette): bool => $palette['key'] !== '')
            ->values();
    }

    private function seasonalHubs()
    {
        return collect((array) config('nova_cards.seasonal_hubs', []))
            ->map(fn (array $hub): array => [
                'key' => (string) ($hub['key'] ?? ''),
                'label' => (string) ($hub['label'] ?? str((string) ($hub['key'] ?? ''))->replace('-', ' ')->title()),
                'tag_slugs' => array_values((array) ($hub['tag_slugs'] ?? [])),
            ])
            ->filter(fn (array $hub): bool => $hub['key'] !== '')
            ->values();
    }
}
