<?php

namespace App\Http\Controllers;

use App\Models\Badge;
use App\Models\CommunityBan;
use App\Models\CommunityCategory;
use App\Models\CommunityComment;
use App\Models\CommunityFollow;
use App\Models\CommunityLike;
use App\Models\CommunityMessage;
use App\Models\CommunityPost;
use App\Models\CommunityPostImage;
use App\Models\CommunityReport;
use App\Models\CommunitySetting;
use App\Models\User;
use App\Services\DisciplineScoreService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class CommunityController extends Controller
{
    public function index(Request $request): View
    {
        $settings = CommunitySetting::current();
        abort_unless($settings->community_enabled, 404);
        abort_if(! $settings->allow_public_viewing && ! Auth::check(), 403);

        CommunityCategory::seedDefaults();

        $posts = CommunityPost::query()
            ->visible()
            ->with(['user', 'category', 'images'])
            ->withCount(['comments', 'likes'])
            ->when($request->filled('category'), fn ($query) => $query->whereHas('category', fn ($query) => $query->where('slug', $request->string('category'))))
            ->when($request->filled('search'), fn ($query) => $query->where(fn ($query) => $query
                ->where('title', 'like', '%'.$request->string('search')->toString().'%')
                ->orWhere('body', 'like', '%'.$request->string('search')->toString().'%')))
            ->orderByDesc('is_featured')
            ->latest()
            ->paginate(12)
            ->withQueryString();

        return view('community.index', [
            'posts' => $posts,
            'categories' => CommunityCategory::activeOrdered(),
            'settings' => $settings,
            'latestMessages' => CommunityMessage::query()->where('is_hidden', false)->with('user')->latest()->limit(5)->get(),
        ]);
    }

    public function create(): View
    {
        $this->ensureCanParticipate();

        return view('community.create', [
            'categories' => CommunityCategory::activeOrdered(),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $this->ensureCanParticipate();
        $settings = CommunitySetting::current();

        $data = $request->validate([
            'community_category_id' => ['required', 'exists:community_categories,id'],
            'title' => ['nullable', 'string', 'min:3', 'max:160'],
            'body' => ['nullable', 'string', 'max:10000'],
            'images' => ['nullable', 'array', 'max:5'],
            'images.*' => ['image', 'mimes:jpg,jpeg,png,webp', 'max:3072'],
        ]);

        if (blank($data['body'] ?? null) && ! $request->hasFile('images')) {
            return back()
                ->withInput()
                ->withErrors(['body' => 'Write a post message, upload at least one image, or add both.']);
        }

        $post = CommunityPost::create([
            'community_category_id' => $data['community_category_id'],
            'user_id' => $request->user()->id,
            'title' => filled($data['title'] ?? null) ? $data['title'] : 'Community post by '.$request->user()->name,
            'body' => $data['body'] ?? '',
            'status' => $settings->post_approval_enabled ? CommunityPost::STATUS_PENDING : CommunityPost::STATUS_PUBLISHED,
            'approved_at' => $settings->post_approval_enabled ? null : now(),
        ]);

        $this->storeImages($request, $post);

        return redirect()
            ->route('community.posts.show', $post)
            ->with('success', $settings->post_approval_enabled ? 'Post submitted for admin approval.' : 'Post published.');
    }

    public function show(CommunityPost $post): View
    {
        $settings = CommunitySetting::current();
        abort_unless($settings->community_enabled, 404);
        abort_if(! $settings->allow_public_viewing && ! Auth::check(), 403);
        abort_if(($post->is_hidden || $post->status !== CommunityPost::STATUS_PUBLISHED) && Auth::id() !== $post->user_id && ! Auth::user()?->isAdmin(), 404);

        return view('community.show', [
            'post' => $post->load(['user', 'category', 'images', 'topLevelComments.user', 'topLevelComments.replies.user'])->loadCount(['likes', 'comments', 'reports']),
            'settings' => $settings,
        ]);
    }

    public function edit(CommunityPost $post): View
    {
        $this->authorizePostOwner($post);

        return view('community.edit', [
            'post' => $post->load('images'),
            'categories' => CommunityCategory::activeOrdered(),
        ]);
    }

    public function update(Request $request, CommunityPost $post): RedirectResponse
    {
        $this->authorizePostOwner($post);

        $data = $request->validate([
            'community_category_id' => ['required', 'exists:community_categories,id'],
            'title' => ['nullable', 'string', 'min:3', 'max:160'],
            'body' => ['nullable', 'string', 'max:10000'],
            'images' => ['nullable', 'array', 'max:5'],
            'images.*' => ['image', 'mimes:jpg,jpeg,png,webp', 'max:3072'],
        ]);

        $remainingSlots = max(0, 5 - $post->images()->count());
        $newImagesCount = count($request->file('images', []));

        if ($newImagesCount > $remainingSlots) {
            return back()
                ->withInput()
                ->withErrors(['images' => 'A community post can have a maximum of 5 images.']);
        }

        if (blank($data['body'] ?? null) && $post->images()->count() === 0 && ! $request->hasFile('images')) {
            return back()
                ->withInput()
                ->withErrors(['body' => 'Write a post message, upload at least one image, or add both.']);
        }

        $data['title'] = filled($data['title'] ?? null) ? $data['title'] : 'Community post by '.$request->user()->name;
        $data['body'] = $data['body'] ?? '';
        unset($data['images']);

        $post->update($data);
        $this->storeImages($request, $post);

        return redirect()->route('community.posts.show', $post)->with('success', 'Post updated.');
    }

    public function destroy(CommunityPost $post): RedirectResponse
    {
        $this->authorizePostOwner($post);
        $post->delete();

        return redirect()->route('community.my-posts')->with('success', 'Post deleted.');
    }

    public function destroyImage(Request $request, CommunityPost $post, CommunityPostImage $image): RedirectResponse
    {
        $this->authorizePostOwner($post);
        abort_unless($image->community_post_id === $post->id, 404);

        if (blank($post->body) && $post->images()->count() <= 1) {
            return back()->withErrors(['images' => 'Add text or another image before deleting the last image.']);
        }

        $image->delete();

        return back()->with('success', 'Image deleted.');
    }

    public function myPosts(Request $request): View
    {
        $this->ensureCanParticipate();

        return view('community.my-posts', [
            'posts' => CommunityPost::query()
                ->where('user_id', $request->user()->id)
                ->with(['category', 'images'])
                ->withCount(['comments', 'likes'])
                ->latest()
                ->paginate(12),
        ]);
    }

    public function comment(Request $request, CommunityPost $post): RedirectResponse|JsonResponse
    {
        $this->ensureCanParticipate();

        $data = $request->validate(['body' => ['required', 'string', 'min:2', 'max:3000']]);
        $comment = $post->comments()->create($data + ['user_id' => $request->user()->id]);
        $comment->load('user');

        if ($request->expectsJson()) {
            return response()->json([
                'success' => true,
                'comment_html' => view('community.partials.comment', ['comment' => $comment, 'post' => $post])->render(),
                'comments_count' => $post->comments()->count(),
            ]);
        }

        return back()->with('success', 'Comment added.');
    }

    public function reply(Request $request, CommunityPost $post, CommunityComment $comment): RedirectResponse
    {
        $this->ensureCanParticipate();
        abort_unless($comment->community_post_id === $post->id, 404);

        $data = $request->validate(['body' => ['required', 'string', 'min:2', 'max:3000']]);
        $post->comments()->create($data + ['user_id' => $request->user()->id, 'parent_id' => $comment->id]);

        return back()->with('success', 'Reply added.');
    }

    public function like(Request $request, CommunityPost $post): RedirectResponse|JsonResponse
    {
        $this->ensureCanParticipate();

        CommunityLike::query()->firstOrCreate([
            'user_id' => $request->user()->id,
            'community_post_id' => $post->id,
        ]);

        if ($request->expectsJson()) {
            return response()->json([
                'success' => true,
                'liked' => true,
                'likes_count' => $post->likes()->count(),
            ]);
        }

        return back();
    }

    public function unlike(Request $request, CommunityPost $post): RedirectResponse|JsonResponse
    {
        CommunityLike::query()
            ->where('user_id', $request->user()->id)
            ->where('community_post_id', $post->id)
            ->delete();

        if ($request->expectsJson()) {
            return response()->json([
                'success' => true,
                'liked' => false,
                'likes_count' => $post->likes()->count(),
            ]);
        }

        return back();
    }

    public function destroyComment(Request $request, CommunityPost $post, CommunityComment $comment): RedirectResponse
    {
        abort_unless($comment->community_post_id === $post->id, 404);
        abort_unless($comment->user_id === $request->user()->id, 403);

        $comment->delete();

        return back()->with('success', 'Comment deleted.');
    }

    public function report(Request $request, CommunityPost $post): RedirectResponse
    {
        $this->ensureCanParticipate();

        $data = $request->validate([
            'reason' => ['required', Rule::in(['Spam', 'Abuse', 'Scam', 'Wrong category', 'Unsafe advice', 'Other'])],
            'details' => ['nullable', 'string', 'max:1000'],
        ]);

        CommunityReport::query()->firstOrCreate(
            ['reporter_id' => $request->user()->id, 'community_post_id' => $post->id],
            $data
        );

        return back()->with('success', 'Report sent to the admin team.');
    }

    public function trader(User $user, DisciplineScoreService $disciplineScoreService): View
    {
        $settings = CommunitySetting::current();
        abort_unless($settings->community_enabled, 404);

        $weeklyScore = $disciplineScoreService->weeklyScore($user);
        $isFollowing = Auth::check() && CommunityFollow::query()
            ->where('follower_id', Auth::id())
            ->where('following_id', $user->id)
            ->exists();

        return view('community.trader', [
            'trader' => $user->loadCount(['communityPosts', 'communityFollowers', 'communityFollowing']),
            'weeklyScore' => $weeklyScore,
            'badges' => Badge::activeOrdered(),
            'isFollowing' => $isFollowing,
        ]);
    }

    public function follow(Request $request, User $user): RedirectResponse
    {
        $this->ensureCanParticipate();
        abort_if($request->user()->id === $user->id, 422);

        CommunityFollow::query()->firstOrCreate([
            'follower_id' => $request->user()->id,
            'following_id' => $user->id,
        ]);

        return back()->with('success', 'Trader followed.');
    }

    public function unfollow(Request $request, User $user): RedirectResponse
    {
        CommunityFollow::query()->where([
            'follower_id' => $request->user()->id,
            'following_id' => $user->id,
        ])->delete();

        return back()->with('success', 'Trader unfollowed.');
    }

    public function chat(): View
    {
        $this->ensureCanParticipate();
        abort_unless(CommunitySetting::current()->chat_enabled, 404);

        return view('community.chat', [
            'messages' => CommunityMessage::query()->where('is_hidden', false)->with('user')->latest()->paginate(30),
        ]);
    }

    public function storeMessage(Request $request): RedirectResponse
    {
        $this->ensureCanParticipate();
        abort_unless(CommunitySetting::current()->chat_enabled, 404);

        $data = $request->validate(['message' => ['required', 'string', 'min:1', 'max:1000']]);
        CommunityMessage::create($data + ['user_id' => $request->user()->id]);

        return back()->with('success', 'Message sent.');
    }

    public function deleteMessage(Request $request, CommunityMessage $message): RedirectResponse
    {
        abort_unless($message->user_id === $request->user()->id, 403);
        $message->delete();

        return back()->with('success', 'Message deleted.');
    }

    private function ensureCanParticipate(): void
    {
        abort_unless(Auth::check(), 403);
        abort_unless(CommunitySetting::current()->community_enabled, 404);
        abort_if(Auth::user()?->isBannedFromCommunity(), 403, 'Your account is blocked from the community.');
    }

    private function authorizePostOwner(CommunityPost $post): void
    {
        $this->ensureCanParticipate();
        abort_unless(Auth::id() === $post->user_id, 403);
    }

    private function storeImages(Request $request, CommunityPost $post): void
    {
        foreach ($request->file('images', []) as $image) {
            $path = $image->store('community-posts', 'public');

            $post->images()->create([
                'image_path' => $path,
                'original_name' => Str::limit($image->getClientOriginalName(), 240, ''),
                'file_size' => $image->getSize() ?: 0,
            ]);
        }
    }
}
