<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Services\Activity\UserActivityService;
use App\Models\Artwork;
use App\Models\ArtworkComment;
use App\Models\User;
use App\Models\UserMention;
use App\Notifications\ArtworkCommentedNotification;
use App\Notifications\ArtworkMentionedNotification;
use App\Services\ContentSanitizer;
use App\Support\AvatarUrl;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Gate;

/**
 * Artwork comment CRUD.
 *
 * POST   /api/artworks/{artworkId}/comments          → store
 * PUT    /api/artworks/{artworkId}/comments/{id}     → update (own comment)
 * DELETE /api/artworks/{artworkId}/comments/{id}     → delete (own or admin)
 * GET    /api/artworks/{artworkId}/comments          → list (paginated)
 */
class ArtworkCommentController extends Controller
{
    private const MAX_LENGTH = 10_000;

    // ─────────────────────────────────────────────────────────────────────────
    // List
    // ─────────────────────────────────────────────────────────────────────────

    public function index(Request $request, int $artworkId): JsonResponse
    {
        $artwork = Artwork::public()->published()->findOrFail($artworkId);

        $page     = max(1, (int) $request->query('page', 1));
        $perPage  = 20;

        // Only fetch top-level comments (no parent). Replies are recursively eager-loaded.
        $comments = ArtworkComment::with([
                'user', 'user.profile',
                'approvedReplies',
            ])
            ->where('artwork_id', $artwork->id)
            ->where('is_approved', true)
            ->whereNull('parent_id')
            ->orderByDesc('created_at')
            ->paginate($perPage, ['*'], 'page', $page);

        $userId = $request->user()?->id;
        $items  = $comments->getCollection()->map(fn ($c) => $this->formatComment($c, $userId, true));

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

    // ─────────────────────────────────────────────────────────────────────────
    // Store
    // ─────────────────────────────────────────────────────────────────────────

    public function store(Request $request, int $artworkId): JsonResponse
    {
        $artwork = Artwork::public()->published()->findOrFail($artworkId);

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

        $raw = $request->input('content');
        $parentId = $request->input('parent_id');

        // If replying, validate parent belongs to same artwork and is approved
        if ($parentId) {
            $parent = ArtworkComment::where('artwork_id', $artwork->id)
                ->where('is_approved', true)
                ->find($parentId);

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

        // Validate markdown-lite content
        $errors = ContentSanitizer::validate($raw);
        if ($errors) {
            return response()->json(['errors' => ['content' => $errors]], 422);
        }

        $rendered = ContentSanitizer::render($raw);

        $comment = ArtworkComment::create([
            'artwork_id'       => $artwork->id,
            'user_id'          => $request->user()->id,
            'parent_id'        => $parentId,
            'content'          => $raw,      // legacy column (plain text fallback)
            'raw_content'      => $raw,
            'rendered_content' => $rendered,
            'is_approved'      => true,       // auto-approve; extend with moderation as needed
        ]);

        // Bust the comments cache for this user's 'all' feed
        Cache::forget('comments.latest.all.page1');

        $comment->load(['user', 'user.profile']);
        $this->notifyRecipients($artwork, $comment, $request->user(), $parentId ? (int) $parentId : null);

        // Record activity event (fire-and-forget; never break the response)
        try {
            \App\Models\ActivityEvent::record(
                actorId:    $request->user()->id,
                type:       \App\Models\ActivityEvent::TYPE_COMMENT,
                targetType: \App\Models\ActivityEvent::TARGET_ARTWORK,
                targetId:   $artwork->id,
            );
        } catch (\Throwable) {}

        try {
            app(UserActivityService::class)->logComment(
                (int) $request->user()->id,
                (int) $comment->id,
                $parentId !== null,
                ['artwork_id' => (int) $artwork->id],
            );
        } catch (\Throwable) {}

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

    // ─────────────────────────────────────────────────────────────────────────
    // Update
    // ─────────────────────────────────────────────────────────────────────────

    public function update(Request $request, int $artworkId, int $commentId): JsonResponse
    {
        $comment = ArtworkComment::where('artwork_id', $artworkId)
            ->findOrFail($commentId);

        Gate::authorize('update', $comment);

        $request->validate([
            'content' => ['required', 'string', 'min:1', 'max:' . self::MAX_LENGTH],
        ]);

        $raw    = $request->input('content');
        $errors = ContentSanitizer::validate($raw);
        if ($errors) {
            return response()->json(['errors' => ['content' => $errors]], 422);
        }

        $rendered = ContentSanitizer::render($raw);

        $comment->update([
            'content'          => $raw,
            'raw_content'      => $raw,
            'rendered_content' => $rendered,
        ]);

        Cache::forget('comments.latest.all.page1');

        $comment->load(['user', 'user.profile']);

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

    // ─────────────────────────────────────────────────────────────────────────
    // Delete
    // ─────────────────────────────────────────────────────────────────────────

    public function destroy(Request $request, int $artworkId, int $commentId): JsonResponse
    {
        $comment = ArtworkComment::where('artwork_id', $artworkId)->findOrFail($commentId);

        Gate::authorize('delete', $comment);

        $comment->delete();
        Cache::forget('comments.latest.all.page1');

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

    // ─────────────────────────────────────────────────────────────────────────
    // Helpers
    // ─────────────────────────────────────────────────────────────────────────

    private function formatComment(ArtworkComment $c, ?int $currentUserId, bool $includeReplies = false): array
    {
        $user     = $c->user;
        $userId   = (int) ($c->user_id ?? 0);
        $avatarHash = $user?->profile?->avatar_hash ?? null;

        $data = [
            'id'               => $c->id,
            'parent_id'        => $c->parent_id,
            'raw_content'      => $c->raw_content ?? $c->content,
            'rendered_content' => $this->renderCommentContent($c),
            'created_at'       => $c->created_at?->toIso8601String(),
            'time_ago'         => $c->created_at ? Carbon::parse($c->created_at)->diffForHumans() : null,
            'can_edit'         => $currentUserId === $userId,
            'can_delete'       => $currentUserId === $userId,
            'user' => [
                'id'          => $userId,
                'username'    => $user?->username,
                'display'     => $user?->username ?? $user?->name ?? 'User',
                'profile_url' => $user?->username ? '/@' . $user->username : '/profile/' . $userId,
                'avatar_url'  => AvatarUrl::forUser($userId, $avatarHash, 64),
                'level'       => (int) ($user?->level ?? 1),
                'rank'        => (string) ($user?->rank ?? 'Newbie'),
            ],
        ];

        if ($includeReplies && $c->relationLoaded('approvedReplies')) {
            $data['replies'] = $c->approvedReplies->map(fn ($r) => $this->formatComment($r, $currentUserId, true))->values()->toArray();
        } elseif ($includeReplies && $c->relationLoaded('replies')) {
            $data['replies'] = $c->replies->map(fn ($r) => $this->formatComment($r, $currentUserId, true))->values()->toArray();
        } else {
            $data['replies'] = [];
        }

        return $data;
    }

    private function renderCommentContent(ArtworkComment $comment): string
    {
        $rawContent = (string) ($comment->raw_content ?? $comment->content ?? '');
        $renderedContent = $comment->rendered_content;

        if (! is_string($renderedContent) || trim($renderedContent) === '') {
            $renderedContent = $rawContent !== ''
                ? ContentSanitizer::render($rawContent)
                : nl2br(e(strip_tags((string) ($comment->content ?? ''))));
        }

        return ContentSanitizer::sanitizeRenderedHtml(
            $renderedContent,
            $this->commentAuthorCanPublishLinks($comment)
        );
    }

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

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

    private function notifyRecipients(Artwork $artwork, ArtworkComment $comment, User $actor, ?int $parentId): void
    {
        $notifiedUserIds = [];
        $creatorId = (int) ($artwork->user_id ?? 0);

        if ($creatorId > 0 && $creatorId !== (int) $actor->id) {
            $creator = User::query()->find($creatorId);
            if ($creator) {
                $creator->notify(new ArtworkCommentedNotification($artwork, $comment, $actor));
                $notifiedUserIds[] = (int) $creator->id;
            }
        }

        if ($parentId) {
            $parentUserId = (int) (ArtworkComment::query()->whereKey($parentId)->value('user_id') ?? 0);
            if ($parentUserId > 0 && $parentUserId !== (int) $actor->id && ! in_array($parentUserId, $notifiedUserIds, true)) {
                $parentUser = User::query()->find($parentUserId);
                if ($parentUser) {
                    $parentUser->notify(new ArtworkCommentedNotification($artwork, $comment, $actor));
                    $notifiedUserIds[] = (int) $parentUser->id;
                }
            }
        }

        User::query()
            ->whereIn(
                'id',
                UserMention::query()
                    ->where('comment_id', (int) $comment->id)
                    ->pluck('mentioned_user_id')
                    ->map(fn ($id) => (int) $id)
                    ->unique()
                    ->all()
            )
            ->get()
            ->each(function (User $mentionedUser) use ($artwork, $comment, $actor): void {
                if ((int) $mentionedUser->id === (int) $actor->id) {
                    return;
                }

                $mentionedUser->notify(new ArtworkMentionedNotification($artwork, $comment, $actor));
            });
    }
}
