<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\NewsArticleComment;
use App\Models\User;
use App\Services\News\NewsArticleCommentService;
use App\Support\AvatarUrl;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use cPad\Plugins\News\Models\NewsArticle;

final class NewsArticleCommentController extends Controller
{
    private const MAX_LENGTH = 4_000;

    public function __construct(private readonly NewsArticleCommentService $comments)
    {
    }

    public function index(Request $request, int $articleId): JsonResponse
    {
        $article = $this->resolveArticle($articleId);
        $page = max(1, (int) $request->query('page', 1));
        $perPage = 20;

        $comments = NewsArticleComment::query()
            ->with(['user.profile', 'visibleReplies'])
            ->where('article_id', $article->id)
            ->where('status', 'visible')
            ->whereNull('parent_id')
            ->orderByDesc('created_at')
            ->orderByDesc('id')
            ->paginate($perPage, ['*'], 'page', $page);

        $viewer = $request->user();
        $items = $comments->getCollection()->map(
            fn (NewsArticleComment $comment): array => $this->formatComment($comment, $viewer, $article, true)
        );

        return response()->json([
            'data' => $items,
            'meta' => [
                'current_page' => $comments->currentPage(),
                'last_page' => $comments->lastPage(),
                'total' => $comments->total(),
                'per_page' => $comments->perPage(),
            ],
        ]);
    }

    public function store(Request $request, int $articleId): JsonResponse
    {
        $article = $this->resolveArticle($articleId);

        $data = $request->validate([
            'content' => ['required', 'string', 'min:2', 'max:' . self::MAX_LENGTH],
            'parent_id' => ['nullable', 'integer', 'exists:news_article_comments,id'],
        ]);

        $parent = null;

        if (! empty($data['parent_id'])) {
            $parent = NewsArticleComment::query()
                ->where('article_id', $article->id)
                ->where('status', 'visible')
                ->find((int) $data['parent_id']);

            if (! $parent) {
                return response()->json([
                    'errors' => [
                        'content' => ['The comment you are replying to is no longer available.'],
                    ],
                ], 422);
            }
        }

        $comment = $this->comments->create($article, $request->user(), (string) $data['content'], $parent);

        return response()->json([
            'data' => $this->formatComment($comment, $request->user(), $article, false),
        ], 201);
    }

    public function destroy(Request $request, int $articleId, int $commentId): JsonResponse
    {
        $article = $this->resolveArticle($articleId);

        $comment = NewsArticleComment::query()
            ->where('article_id', $article->id)
            ->findOrFail($commentId);

        $this->comments->delete($comment, $request->user());

        return response()->json([
            'message' => 'Comment deleted.',
        ]);
    }

    private function resolveArticle(int $articleId): NewsArticle
    {
        return NewsArticle::query()
            ->published()
            ->whereKey($articleId)
            ->firstOrFail();
    }

    private function formatComment(NewsArticleComment $comment, ?User $viewer, NewsArticle $article, bool $includeReplies = false): array
    {
        $user = $comment->user;
        $displayName = $user?->username ?? $user?->name ?? $comment->author_name ?? 'Former member';
        $avatarHash = $user?->profile?->avatar_hash ?? null;

        $data = [
            'id' => $comment->id,
            'parent_id' => $comment->parent_id,
            'raw_content' => (string) ($comment->body ?? ''),
            'rendered_content' => $comment->getDisplayHtml(),
            'created_at' => $comment->created_at?->toIso8601String(),
            'time_ago' => $comment->created_at ? Carbon::parse($comment->created_at)->diffForHumans() : null,
            'can_delete' => $this->canDelete($comment, $article, $viewer),
            'user' => [
                'id' => $user?->id,
                'username' => $user?->username,
                'display' => $displayName,
                'profile_url' => $user?->username ? '/@' . $user->username : null,
                'avatar_url' => $user ? AvatarUrl::forUser((int) $user->id, $avatarHash, 64) : null,
                'level' => (int) ($user?->level ?? 1),
                'rank' => (string) ($user?->rank ?? 'Newbie'),
            ],
            'replies' => [],
        ];

        if ($includeReplies) {
            $replies = $comment->relationLoaded('visibleReplies')
                ? $comment->visibleReplies
                : collect();

            $data['replies'] = $replies
                ->map(fn (NewsArticleComment $reply): array => $this->formatComment($reply, $viewer, $article, true))
                ->values()
                ->all();
        }

        return $data;
    }

    private function canDelete(NewsArticleComment $comment, NewsArticle $article, ?User $viewer): bool
    {
        if (! $viewer) {
            return false;
        }

        return (int) $comment->user_id === (int) $viewer->id
            || (int) $article->author_id === (int) $viewer->id
            || $viewer->isAdmin()
            || $viewer->isModerator();
    }
}