<?php
namespace App\Http\Resources;

use App\Models\WorldRelation;
use App\Models\WorldSubmission;
use App\Models\World;
use App\Services\ArtworkEvolutionService;
use App\Services\ContentSanitizer;
use App\Services\Maturity\ArtworkMaturityService;
use App\Services\ThumbnailPresenter;
use App\Services\Worlds\WorldRewardService;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class ArtworkResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     */
    public function toArray($request): array
    {
        $this->resource->loadMissing(['group', 'uploadedBy.profile', 'primaryAuthor.profile', 'contributors.user.profile', 'worldSubmissions.world', 'worldRewardGrants.world']);

        $md = ThumbnailPresenter::present($this->resource, 'md');
        $lg = ThumbnailPresenter::present($this->resource, 'lg');
        $xl = ThumbnailPresenter::present($this->resource, 'xl');
        $sq = ThumbnailPresenter::present($this->resource, 'sq');
        $screenshots = $this->resolveScreenshotAssets();

        $canonicalSlug = \Illuminate\Support\Str::slug((string) ($this->slug ?: $this->title));
        if ($canonicalSlug === '') {
            $canonicalSlug = (string) $this->id;
        }

        $followerCount = 0;
        if (!empty($this->user?->id)) {
            if (Schema::hasTable('user_statistics')) {
                $followerCount = (int) DB::table('user_statistics')
                    ->where('user_id', (int) $this->user->id)
                    ->value('followers_count');
            }

            // Legacy fallback for environments where new tables are unavailable.
            if (($followerCount <= 0) && Schema::hasTable('friends_list')) {
                $followerCount = (int) DB::table('friends_list')
                    ->where('friend_id', (int) $this->user->id)
                    ->count();
            }
        }

        $viewerId = (int) optional($request->user())->id;
        $isLiked = false;
        $isFavorited = false;
        $isBookmarked = false;
        $isFollowing = false;
        $isFollowingGroup = false;
        $viewerAward = null;
        $isOwner = $viewerId > 0 && $viewerId === (int) ($this->user?->id ?? 0);

        $bookmarksCount = Schema::hasTable('artwork_bookmarks')
            ? (int) DB::table('artwork_bookmarks')->where('artwork_id', (int) $this->id)->count()
            : 0;

        if ($viewerId > 0) {
            if (Schema::hasTable('artwork_likes')) {
                $isLiked = DB::table('artwork_likes')
                    ->where('user_id', $viewerId)
                    ->where('artwork_id', (int) $this->id)
                    ->exists();
            }

            if (Schema::hasTable('artwork_bookmarks')) {
                $isBookmarked = DB::table('artwork_bookmarks')
                    ->where('user_id', $viewerId)
                    ->where('artwork_id', (int) $this->id)
                    ->exists();
            }

            $isFavorited = DB::table('artwork_favourites')
                ->where('user_id', $viewerId)
                ->where('artwork_id', (int) $this->id)
                ->exists();

            if (!empty($this->user?->id)) {
                if (Schema::hasTable('user_followers')) {
                    $isFollowing = DB::table('user_followers')
                        ->where('user_id', (int) $this->user->id)
                        ->where('follower_id', $viewerId)
                        ->exists();
                } elseif (Schema::hasTable('friends_list')) {
                    // Legacy fallback only.
                    $isFollowing = DB::table('friends_list')
                        ->where('user_id', $viewerId)
                        ->where('friend_id', (int) $this->user->id)
                        ->exists();
                }
            }

            if (!empty($this->group?->id) && Schema::hasTable('group_follows')) {
                $isFollowingGroup = DB::table('group_follows')
                    ->where('group_id', (int) $this->group->id)
                    ->where('user_id', $viewerId)
                    ->exists();
            }

            if (Schema::hasTable('artwork_medals')) {
                $viewerAward = DB::table('artwork_medals')
                    ->where('user_id', $viewerId)
                    ->where('artwork_id', (int) $this->id)
                    ->value('medal_type');
            }
        }

        $decode = static fn (?string $v): string => html_entity_decode((string) ($v ?? ''), ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $mapUser = static function ($user): ?array {
            if (! $user) {
                return null;
            }

            return [
                'id' => (int) ($user->id ?? 0),
                'name' => html_entity_decode((string) ($user->name ?? ''), ENT_QUOTES | ENT_HTML5, 'UTF-8'),
                'username' => (string) ($user->username ?? ''),
                'profile_url' => ! empty($user->username) ? '/@' . $user->username : null,
                'avatar_url' => $user->profile?->avatar_url,
            ];
        };

        $publisher = $this->group
            ? [
                'type' => 'group',
                'id' => (int) $this->group->id,
                'name' => (string) $this->group->name,
                'slug' => (string) $this->group->slug,
                'headline' => (string) ($this->group->headline ?? ''),
                'avatar_url' => $this->group->avatarUrl(),
                'profile_url' => $this->group->publicUrl(),
                'followers_count' => (int) ($this->group->followers_count ?? 0),
                'follow_url' => route('groups.follow', ['group' => $this->group]),
                'unfollow_url' => route('groups.unfollow', ['group' => $this->group]),
            ]
            : [
                'type' => 'user',
                'id' => (int) ($this->user?->id ?? 0),
                'name' => html_entity_decode((string) ($this->user?->name ?? ''), ENT_QUOTES | ENT_HTML5, 'UTF-8'),
                'slug' => (string) ($this->user?->username ?? ''),
                'headline' => '',
                'avatar_url' => $this->user?->profile?->avatar_url,
                'profile_url' => $this->user?->username ? '/@' . $this->user->username : null,
                'followers_count' => $followerCount,
                'follow_url' => null,
                'unfollow_url' => null,
            ];

        $primaryAuthor = $mapUser($this->primaryAuthor ?: $this->user);
        $uploadedBy = $mapUser($this->uploadedBy ?: $this->user);
        $contributors = $this->contributors
            ->map(function ($contributor) use ($mapUser): ?array {
                $user = $mapUser($contributor->user);
                if (! $user) {
                    return null;
                }

                return array_merge($user, [
                    'credit_role' => $contributor->credit_role,
                    'is_primary' => (bool) $contributor->is_primary,
                ]);
            })
            ->filter()
            ->values();

        return [
            'id' => (int) $this->id,
            'slug' => (string) $this->slug,
            'title' => $decode($this->title),
            'description' => $decode($this->description),
            'description_html' => $this->renderDescriptionHtml(),
            'dimensions' => [
                'width' => (int) ($this->width ?? 0),
                'height' => (int) ($this->height ?? 0),
            ],
            'published_at' => optional($this->published_at)->toIsoString(),
            'canonical_url' => route('art.show', ['id' => (int) $this->id, 'slug' => $canonicalSlug]),
            'thumbs' => [
                'md' => $md,
                'lg' => $lg,
                'xl' => $xl,
                'sq' => $sq,
            ],
            'file' => [
                'url' => $lg['url'] ?? null,
                'srcset' => ThumbnailPresenter::srcsetForArtwork($this->resource),
                'mime_type' => 'image/webp',
            ],
            'screenshots' => $screenshots,
            'user' => [
                'id' => (int) ($this->user?->id ?? 0),
                'name' => html_entity_decode((string) ($this->user?->name ?? ''), ENT_QUOTES | ENT_HTML5, 'UTF-8'),
                'username' => (string) ($this->user?->username ?? ''),
                'profile_url' => $this->user?->username ? '/@' . $this->user->username : null,
                'avatar_url' => $this->user?->profile?->avatar_url,
                'level' => (int) ($this->user?->level ?? 1),
                'rank' => (string) ($this->user?->rank ?? 'Newbie'),
                'followers_count' => $followerCount,
            ],
            'publisher' => $publisher,
            'credits' => [
                'uploaded_by' => $uploadedBy,
                'primary_author' => $primaryAuthor,
                'contributors' => $contributors,
            ],
            'viewer' => [
                'is_bookmarked' => $isBookmarked,
                'is_liked' => $isLiked,
                'is_favorited' => $isFavorited,
                'is_following_author' => $isFollowing,
                'is_following_group' => $isFollowingGroup,
                'is_following_publisher' => $publisher['type'] === 'group' ? $isFollowingGroup : $isFollowing,
                'is_authenticated' => $viewerId > 0,
                'is_owner' => $isOwner,
                'id' => $viewerId > 0 ? $viewerId : null,
            ],
            'management' => [
                'analytics_url' => $isOwner ? route('studio.artworks.analytics', ['id' => (int) $this->id]) : null,
            ],
            'stats' => [
                'bookmarks' => $bookmarksCount,
                'views' => (int) ($this->stats?->views ?? 0),
                'downloads' => (int) ($this->stats?->downloads ?? 0),
                'favorites' => (int) ($this->stats?->favorites ?? 0),
                'likes' => (int) ($this->stats?->rating_count ?? 0),
            ],
            'awards' => [
                'gold' => (int) ($this->awardStat?->gold_count ?? 0),
                'silver' => (int) ($this->awardStat?->silver_count ?? 0),
                'bronze' => (int) ($this->awardStat?->bronze_count ?? 0),
                'score' => (int) ($this->awardStat?->score_total ?? 0),
                'score_7d' => (int) ($this->awardStat?->score_7d ?? 0),
                'score_30d' => (int) ($this->awardStat?->score_30d ?? 0),
                'last_medaled_at' => optional($this->awardStat?->last_medaled_at)->toIsoString(),
                'viewer_award' => $viewerAward,
            ],
            'medals' => [
                'gold' => (int) ($this->awardStat?->gold_count ?? 0),
                'silver' => (int) ($this->awardStat?->silver_count ?? 0),
                'bronze' => (int) ($this->awardStat?->bronze_count ?? 0),
                'score' => (int) ($this->awardStat?->score_total ?? 0),
                'score_7d' => (int) ($this->awardStat?->score_7d ?? 0),
                'score_30d' => (int) ($this->awardStat?->score_30d ?? 0),
                'last_medaled_at' => optional($this->awardStat?->last_medaled_at)->toIsoString(),
                'current_user_medal' => $viewerAward,
                'viewer_award' => $viewerAward,
            ],
            'maturity' => app(ArtworkMaturityService::class)->presentation($this->resource, $request->user()),
            'evolution' => app(ArtworkEvolutionService::class)->publicPayload($this->resource, $request->user()),
            'world_participation' => $this->resolveWorldParticipation(),
            'categories' => $this->categories->map(fn ($category) => [
                'id' => (int) $category->id,
                'slug' => (string) $category->slug,
                'name' => html_entity_decode((string) $category->name, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
                'content_type_slug' => (string) ($category->contentType?->slug ?? ''),
                'url' => $category->contentType ? $category->url : null,
                'parent' => $category->parent ? [
                    'id' => (int) $category->parent->id,
                    'slug' => (string) $category->parent->slug,
                    'name' => html_entity_decode((string) $category->parent->name, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
                    'content_type_slug' => (string) ($category->parent->contentType?->slug ?? ''),
                    'url' => $category->parent->contentType ? $category->parent->url : null,
                ] : null,
            ])->values(),
            'tags' => $this->tags->map(fn ($tag) => [
                'id' => (int) $tag->id,
                'slug' => (string) $tag->slug,
                'name' => html_entity_decode((string) $tag->name, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
            ])->values(),
        ];
    }

    private function resolveScreenshotAssets(): array
    {
        if (! Schema::hasTable('artwork_files')) {
            return [];
        }

        return DB::table('artwork_files')
            ->where('artwork_id', (int) $this->id)
            ->where('variant', 'like', 'shot%')
            ->orderBy('variant')
            ->get(['variant', 'path', 'mime', 'size'])
            ->map(function ($row, int $index): array {
                $path = (string) ($row->path ?? '');
                $url = $this->objectUrl($path);

                return [
                    'id' => (string) ($row->variant ?? ('shot' . ($index + 1))),
                    'variant' => (string) ($row->variant ?? ''),
                    'label' => 'Screenshot ' . ($index + 1),
                    'url' => $url,
                    'thumb_url' => $url,
                    'mime_type' => (string) ($row->mime ?? 'image/jpeg'),
                    'size' => (int) ($row->size ?? 0),
                ];
            })
            ->filter(fn (array $item): bool => $item['url'] !== null)
            ->values()
            ->all();
    }

    private function objectUrl(string $path): ?string
    {
        $trimmedPath = trim($path, '/');
        if ($trimmedPath === '') {
            return null;
        }

        $base = rtrim((string) config('cdn.files_url', 'https://files.skinbase.org'), '/');

        return $base . '/' . $trimmedPath;
    }

    private function renderDescriptionHtml(): string
    {
        $rawDescription = (string) ($this->description ?? '');

        if (trim($rawDescription) === '') {
            return '';
        }

        if (! $this->authorCanPublishLinks()) {
            return nl2br(e(ContentSanitizer::stripToPlain($rawDescription)));
        }

        return ContentSanitizer::render($rawDescription);
    }

    private function resolveWorldParticipation(): array
    {
        $items = collect();
        $participationWorlds = collect();

        if (Schema::hasTable('world_relations') && Schema::hasTable('worlds')) {
            $relations = WorldRelation::query()
                ->with('world')
                ->where('related_type', WorldRelation::TYPE_ARTWORK)
                ->where('related_id', (int) $this->id)
                ->get()
                ->filter(fn (WorldRelation $relation): bool => $relation->world !== null && $relation->world->isPubliclyVisible())
                ->values();

            $participationWorlds = $participationWorlds->concat($relations->pluck('world')->filter());

            $items = $items->concat(
                $relations->map(function (WorldRelation $relation): array {
                    $world = $relation->world;

                    return [
                        'world_id' => (int) $relation->world_id,
                        'world_title' => (string) $world->title,
                        'world_slug' => (string) $world->slug,
                        'world_url' => $world->publicUrl(),
                        'badge_label' => 'Part of ' . $world->title,
                        'status' => 'curated',
                        'status_label' => 'Curated',
                        'tone' => 'curated',
                        'sort_priority' => 1,
                    ];
                })
            );
        }

        if (Schema::hasTable('world_submissions')) {
            $liveSubmissions = $this->worldSubmissions
                ->filter(function (WorldSubmission $submission): bool {
                    return (string) $submission->status === WorldSubmission::STATUS_LIVE
                        && $submission->world !== null
                        && $submission->world->isPubliclyVisible();
                })
                ->values();

            $participationWorlds = $participationWorlds->concat($liveSubmissions->pluck('world')->filter());
            World::primeCanonicalEditionIds($participationWorlds->pluck('recurrence_key')->all());

            $items = $items->concat(
                $liveSubmissions->map(function (WorldSubmission $submission): array {
                    $world = $submission->world;
                    $isFeatured = (bool) $submission->is_featured;

                    return [
                        'world_id' => (int) $submission->world_id,
                        'world_title' => (string) $world->title,
                        'world_slug' => (string) $world->slug,
                        'world_url' => $world->publicUrl(),
                        'badge_label' => ($isFeatured ? 'Featured in ' : 'Part of ') . $world->title,
                        'status' => (string) $submission->status,
                        'status_label' => $isFeatured ? 'Featured' : 'Community submission',
                        'tone' => $isFeatured ? 'featured' : 'community',
                        'sort_priority' => $isFeatured ? 0 : 2,
                    ];
                })
            );
        } elseif ($participationWorlds->isNotEmpty()) {
            World::primeCanonicalEditionIds($participationWorlds->pluck('recurrence_key')->all());
        }

        if (Schema::hasTable('world_reward_grants')) {
            $items = $items->concat(app(WorldRewardService::class)->artworkRewardBadges($this->resource));
        }

        return $items
            ->sortBy('sort_priority')
            ->groupBy('world_id')
            ->map(function ($group): array {
                $item = $group->first();
                unset($item['sort_priority']);

                return $item;
            })
            ->sortBy(fn (array $item): string => strtolower((string) $item['world_title']))
            ->values()
            ->all();
    }

    private function authorCanPublishLinks(): bool
    {
        $level = (int) ($this->user?->level ?? 1);
        $rank = strtolower((string) ($this->user?->rank ?? 'Newbie'));

        return $level > 1 && $rank !== 'newbie';
    }
}
