<?php

declare(strict_types=1);

namespace App\Services\Studio;

use App\Models\Artwork;
use App\Models\ArtworkComment;
use App\Models\Collection;
use App\Models\CollectionComment;
use App\Models\NovaCard;
use App\Models\NovaCardComment;
use App\Models\Report;
use App\Models\Story;
use App\Models\StoryComment;
use App\Models\User;
use App\Services\CollectionCommentService;
use App\Services\NovaCards\NovaCardCommentService;
use App\Services\SocialService;
use App\Support\AvatarUrl;
use App\Support\ContentSanitizer;
use App\Support\Moderation\ReportTargetResolver;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

final class CreatorStudioCommentService
{
    public function __construct(
        private readonly NovaCardCommentService $cardComments,
        private readonly CollectionCommentService $collectionComments,
        private readonly SocialService $social,
        private readonly ReportTargetResolver $reports,
    ) {
    }

    /**
     * @param array<string, mixed> $filters
     */
    public function list(User $user, array $filters = []): array
    {
        $module = $this->normalizeModule((string) ($filters['module'] ?? 'all'));
        $query = trim((string) ($filters['q'] ?? ''));
        $page = max(1, (int) ($filters['page'] ?? 1));
        $perPage = min(max((int) ($filters['per_page'] ?? 18), 12), 36);

        $items = $this->artworkComments($user)
            ->concat($this->cardComments($user))
            ->concat($this->collectionComments($user))
            ->concat($this->storyComments($user))
            ->sortByDesc(fn (array $item): int => strtotime((string) ($item['created_at'] ?? '')) ?: 0)
            ->values();

        if ($module !== 'all') {
            $items = $items->where('module', $module)->values();
        }

        if ($query !== '') {
            $needle = mb_strtolower($query);
            $items = $items->filter(function (array $item) use ($needle): bool {
                return collect([
                    $item['author_name'] ?? '',
                    $item['item_title'] ?? '',
                    $item['body'] ?? '',
                    $item['module_label'] ?? '',
                ])->contains(fn ($value): bool => is_string($value) && str_contains(mb_strtolower($value), $needle));
            })->values();
        }

        $total = $items->count();
        $lastPage = max(1, (int) ceil($total / $perPage));
        $page = min($page, $lastPage);

        return [
            'items' => $items->forPage($page, $perPage)->values()->all(),
            'meta' => [
                'current_page' => $page,
                'last_page' => $lastPage,
                'per_page' => $perPage,
                'total' => $total,
            ],
            'filters' => [
                'module' => $module,
                'q' => $query,
            ],
            'module_options' => [
                ['value' => 'all', 'label' => 'All comments'],
                ['value' => 'artworks', 'label' => 'Artworks'],
                ['value' => 'cards', 'label' => 'Cards'],
                ['value' => 'collections', 'label' => 'Collections'],
                ['value' => 'stories', 'label' => 'Stories'],
            ],
        ];
    }

    public function reply(User $user, string $module, int $commentId, string $content): void
    {
        $trimmed = trim($content);
        abort_if($trimmed === '' || mb_strlen($trimmed) > 10000, 422, 'Reply content is invalid.');

        match ($this->normalizeModule($module)) {
            'artworks' => $this->replyToArtworkComment($user, $commentId, $trimmed),
            'cards' => $this->replyToCardComment($user, $commentId, $trimmed),
            'collections' => $this->replyToCollectionComment($user, $commentId, $trimmed),
            'stories' => $this->replyToStoryComment($user, $commentId, $trimmed),
            default => abort(404),
        };
    }

    public function moderate(User $user, string $module, int $commentId): void
    {
        match ($this->normalizeModule($module)) {
            'artworks' => $this->deleteArtworkComment($user, $commentId),
            'cards' => $this->deleteCardComment($user, $commentId),
            'collections' => $this->deleteCollectionComment($user, $commentId),
            'stories' => $this->deleteStoryComment($user, $commentId),
            default => abort(404),
        };
    }

    public function report(User $user, string $module, int $commentId, string $reason, ?string $details = null): array
    {
        $targetType = match ($this->normalizeModule($module)) {
            'artworks' => 'artwork_comment',
            'cards' => 'nova_card_comment',
            'collections' => 'collection_comment',
            'stories' => 'story_comment',
            default => abort(404),
        };

        $this->reports->validateForReporter($user, $targetType, $commentId);

        $report = Report::query()->create([
            'reporter_id' => $user->id,
            'target_type' => $targetType,
            'target_id' => $commentId,
            'reason' => trim($reason),
            'details' => $details ? trim($details) : null,
            'status' => 'open',
        ]);

        return [
            'id' => (int) $report->id,
            'status' => (string) $report->status,
        ];
    }

    private function artworkComments(User $user)
    {
        return ArtworkComment::query()
            ->with(['user.profile', 'artwork'])
            ->whereNull('deleted_at')
            ->whereHas('artwork', fn ($query) => $query->where('user_id', $user->id))
            ->latest('created_at')
            ->limit(120)
            ->get()
            ->map(fn (ArtworkComment $comment): array => [
                'id' => 'artworks:' . $comment->id,
                'comment_id' => (int) $comment->id,
                'module' => 'artworks',
                'module_label' => 'Artworks',
                'target_type' => 'artwork_comment',
                'author_name' => $comment->user?->name ?: $comment->user?->username ?: 'Creator',
                'author_username' => $comment->user?->username,
                'author_avatar_url' => AvatarUrl::forUser((int) ($comment->user?->id ?? 0), $comment->user?->profile?->avatar_hash, 64),
                'item_title' => $comment->artwork?->title,
                'item_id' => (int) ($comment->artwork?->id ?? 0),
                'body' => (string) ($comment->raw_content ?: $comment->content),
                'created_at' => $comment->created_at?->toIso8601String(),
                'time_ago' => $comment->created_at?->diffForHumans(),
                'context_url' => $comment->artwork
                    ? route('art.show', ['id' => $comment->artwork->id, 'slug' => $comment->artwork->slug]) . '#comment-' . $comment->id
                    : route('studio.comments'),
                'preview_url' => $comment->artwork
                    ? route('art.show', ['id' => $comment->artwork->id, 'slug' => $comment->artwork->slug])
                    : null,
                'reply_supported' => true,
                'moderate_supported' => true,
                'report_supported' => true,
            ]);
    }

    private function cardComments(User $user)
    {
        return NovaCardComment::query()
            ->with(['user.profile', 'card'])
            ->whereNull('deleted_at')
            ->whereHas('card', fn ($query) => $query->where('user_id', $user->id))
            ->latest('created_at')
            ->limit(120)
            ->get()
            ->map(fn (NovaCardComment $comment): array => [
                'id' => 'cards:' . $comment->id,
                'comment_id' => (int) $comment->id,
                'module' => 'cards',
                'module_label' => 'Cards',
                'target_type' => 'nova_card_comment',
                'author_name' => $comment->user?->name ?: $comment->user?->username ?: 'Creator',
                'author_username' => $comment->user?->username,
                'author_avatar_url' => AvatarUrl::forUser((int) ($comment->user?->id ?? 0), $comment->user?->profile?->avatar_hash, 64),
                'item_title' => $comment->card?->title,
                'item_id' => (int) ($comment->card?->id ?? 0),
                'body' => (string) $comment->body,
                'created_at' => $comment->created_at?->toIso8601String(),
                'time_ago' => $comment->created_at?->diffForHumans(),
                'context_url' => $comment->card ? $comment->card->publicUrl() . '#comment-' . $comment->id : route('studio.comments'),
                'preview_url' => $comment->card ? route('studio.cards.preview', ['id' => $comment->card->id]) : null,
                'reply_supported' => true,
                'moderate_supported' => true,
                'report_supported' => true,
            ]);
    }

    private function collectionComments(User $user)
    {
        return CollectionComment::query()
            ->with(['user.profile', 'collection'])
            ->whereNull('deleted_at')
            ->whereHas('collection', fn ($query) => $query->where('user_id', $user->id))
            ->latest('created_at')
            ->limit(120)
            ->get()
            ->map(fn (CollectionComment $comment): array => [
                'id' => 'collections:' . $comment->id,
                'comment_id' => (int) $comment->id,
                'module' => 'collections',
                'module_label' => 'Collections',
                'target_type' => 'collection_comment',
                'author_name' => $comment->user?->name ?: $comment->user?->username ?: 'Creator',
                'author_username' => $comment->user?->username,
                'author_avatar_url' => AvatarUrl::forUser((int) ($comment->user?->id ?? 0), $comment->user?->profile?->avatar_hash, 64),
                'item_title' => $comment->collection?->title,
                'item_id' => (int) ($comment->collection?->id ?? 0),
                'body' => (string) $comment->body,
                'created_at' => $comment->created_at?->toIso8601String(),
                'time_ago' => $comment->created_at?->diffForHumans(),
                'context_url' => $comment->collection
                    ? route('profile.collections.show', ['username' => strtolower((string) $user->username), 'slug' => $comment->collection->slug]) . '#comment-' . $comment->id
                    : route('studio.comments'),
                'preview_url' => $comment->collection
                    ? route('profile.collections.show', ['username' => strtolower((string) $user->username), 'slug' => $comment->collection->slug])
                    : null,
                'reply_supported' => true,
                'moderate_supported' => true,
                'report_supported' => true,
            ]);
    }

    private function storyComments(User $user)
    {
        return StoryComment::query()
            ->with(['user.profile', 'story'])
            ->whereNull('deleted_at')
            ->whereHas('story', fn ($query) => $query->where('creator_id', $user->id))
            ->latest('created_at')
            ->limit(120)
            ->get()
            ->map(fn (StoryComment $comment): array => [
                'id' => 'stories:' . $comment->id,
                'comment_id' => (int) $comment->id,
                'module' => 'stories',
                'module_label' => 'Stories',
                'target_type' => 'story_comment',
                'author_name' => $comment->user?->name ?: $comment->user?->username ?: 'Creator',
                'author_username' => $comment->user?->username,
                'author_avatar_url' => AvatarUrl::forUser((int) ($comment->user?->id ?? 0), $comment->user?->profile?->avatar_hash, 64),
                'item_title' => $comment->story?->title,
                'item_id' => (int) ($comment->story?->id ?? 0),
                'body' => (string) ($comment->raw_content ?: $comment->content),
                'created_at' => $comment->created_at?->toIso8601String(),
                'time_ago' => $comment->created_at?->diffForHumans(),
                'context_url' => $comment->story ? route('stories.show', ['slug' => $comment->story->slug]) . '#comment-' . $comment->id : route('studio.comments'),
                'preview_url' => $comment->story ? route('creator.stories.preview', ['story' => $comment->story->id]) : null,
                'reply_supported' => true,
                'moderate_supported' => true,
                'report_supported' => true,
            ]);
    }

    private function replyToArtworkComment(User $user, int $commentId, string $content): void
    {
        $comment = ArtworkComment::query()
            ->with('artwork')
            ->findOrFail($commentId);

        abort_unless((int) ($comment->artwork?->user_id ?? 0) === (int) $user->id, 403);

        $errors = ContentSanitizer::validate($content);
        if ($errors !== []) {
            abort(422, implode(' ', $errors));
        }

        ArtworkComment::query()->create([
            'artwork_id' => $comment->artwork_id,
            'user_id' => $user->id,
            'parent_id' => $comment->id,
            'content' => $content,
            'raw_content' => $content,
            'rendered_content' => ContentSanitizer::render($content),
            'is_approved' => true,
        ]);

        $this->syncArtworkCommentCount((int) $comment->artwork_id);
    }

    private function replyToCardComment(User $user, int $commentId, string $content): void
    {
        $comment = NovaCardComment::query()->with(['card.user'])->findOrFail($commentId);
        abort_unless((int) ($comment->card?->user_id ?? 0) === (int) $user->id, 403);
        $this->cardComments->create($comment->card->loadMissing('user'), $user, $content, $comment);
    }

    private function replyToCollectionComment(User $user, int $commentId, string $content): void
    {
        $comment = CollectionComment::query()->with(['collection.user'])->findOrFail($commentId);
        abort_unless((int) ($comment->collection?->user_id ?? 0) === (int) $user->id, 403);
        $this->collectionComments->create($comment->collection->loadMissing('user'), $user, $content, $comment);
    }

    private function replyToStoryComment(User $user, int $commentId, string $content): void
    {
        $comment = StoryComment::query()->with('story')->findOrFail($commentId);
        abort_unless((int) ($comment->story?->creator_id ?? 0) === (int) $user->id, 403);
        $this->social->addStoryComment($user, $comment->story, $content, $comment->id);
    }

    private function deleteArtworkComment(User $user, int $commentId): void
    {
        $comment = ArtworkComment::query()->with('artwork')->findOrFail($commentId);
        abort_unless((int) ($comment->artwork?->user_id ?? 0) === (int) $user->id, 403);

        if (! $comment->trashed()) {
            $comment->delete();
        }

        $this->syncArtworkCommentCount((int) $comment->artwork_id);
    }

    private function deleteCardComment(User $user, int $commentId): void
    {
        $comment = NovaCardComment::query()->with('card')->findOrFail($commentId);
        abort_unless((int) ($comment->card?->user_id ?? 0) === (int) $user->id, 403);
        $this->cardComments->delete($comment, $user);
    }

    private function deleteCollectionComment(User $user, int $commentId): void
    {
        $comment = CollectionComment::query()->with('collection')->findOrFail($commentId);
        abort_unless((int) ($comment->collection?->user_id ?? 0) === (int) $user->id, 403);
        $this->collectionComments->delete($comment, $user);
    }

    private function deleteStoryComment(User $user, int $commentId): void
    {
        $comment = StoryComment::query()->with('story')->findOrFail($commentId);
        abort_unless((int) ($comment->story?->creator_id ?? 0) === (int) $user->id, 403);
        $this->social->deleteStoryComment($user, $comment);
    }

    private function syncArtworkCommentCount(int $artworkId): void
    {
        $count = ArtworkComment::query()
            ->where('artwork_id', $artworkId)
            ->whereNull('deleted_at')
            ->count();

        if (DB::table('artwork_stats')->where('artwork_id', $artworkId)->exists()) {
            DB::table('artwork_stats')
                ->where('artwork_id', $artworkId)
                ->update(['comments_count' => $count, 'updated_at' => now()]);
        }
    }

    private function normalizeModule(string $module): string
    {
        return in_array($module, ['all', 'artworks', 'cards', 'collections', 'stories'], true)
            ? $module
            : 'all';
    }
}