<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Collection;
use App\Models\CollectionComment;
use App\Models\User;
use App\Support\AvatarUrl;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class CollectionCommentService
{
    public function __construct(
        private readonly NotificationService $notifications,
    ) {
    }

    public function create(Collection $collection, User $actor, string $body, ?CollectionComment $parent = null): CollectionComment
    {
        if (! $collection->canReceiveCommentsFrom($actor)) {
            throw ValidationException::withMessages([
                'collection' => 'Comments are disabled for this collection.',
            ]);
        }

        $comment = CollectionComment::query()->create([
            'collection_id' => $collection->id,
            'user_id' => $actor->id,
            'parent_id' => $parent?->id,
            'body' => trim($body),
            'rendered_body' => nl2br(e(trim($body))),
            'status' => Collection::COMMENT_VISIBLE,
        ]);

        $collection->increment('comments_count');
        $collection->forceFill(['last_activity_at' => now()])->save();

        if (! $collection->isOwnedBy($actor)) {
            $this->notifications->notifyCollectionComment($collection->user, $actor, $collection, $comment);
        }

        return $comment->fresh(['user.profile', 'replies.user.profile']);
    }

    public function delete(CollectionComment $comment, User $actor): void
    {
        if ((int) $comment->user_id !== (int) $actor->id && ! $comment->collection->canBeManagedBy($actor)) {
            throw ValidationException::withMessages([
                'comment' => 'You are not allowed to remove this comment.',
            ]);
        }

        if ($comment->trashed()) {
            return;
        }

        $comment->delete();
        $comment->collection()->decrement('comments_count');
    }

    public function mapComments(Collection $collection, ?User $viewer = null): array
    {
        $comments = $collection->comments()
            ->whereNull('parent_id')
            ->where('status', Collection::COMMENT_VISIBLE)
            ->with(['user.profile', 'replies.user.profile'])
            ->latest()
            ->limit(30)
            ->get();

        return $comments->map(fn (CollectionComment $comment) => $this->mapComment($comment, $viewer))->all();
    }

    private function mapComment(CollectionComment $comment, ?User $viewer = null): array
    {
        $user = $comment->user;

        return [
            'id' => (int) $comment->id,
            'body' => (string) $comment->body,
            'rendered_content' => (string) $comment->rendered_body,
            'time_ago' => $comment->created_at?->diffForHumans(),
            'created_at' => $comment->created_at?->toISOString(),
            'can_delete' => $viewer !== null && ((int) $viewer->id === (int) $comment->user_id || $comment->collection->canBeManagedBy($viewer)),
            'can_report' => $viewer !== null && (int) $viewer->id !== (int) $comment->user_id,
            'user' => [
                'id' => (int) $user->id,
                'display' => (string) ($user->name ?: $user->username),
                'username' => (string) $user->username,
                'level' => (int) ($user->level ?? 0),
                'rank' => (string) ($user->rank ?? ''),
                'avatar_url' => AvatarUrl::forUser((int) $user->id, $user->profile?->avatar_hash, 64),
                'profile_url' => '/@' . Str::lower((string) $user->username),
            ],
            'replies' => $comment->replies
                ->where('status', Collection::COMMENT_VISIBLE)
                ->map(fn (CollectionComment $reply) => $this->mapComment($reply, $viewer))
                ->values()
                ->all(),
        ];
    }
}
