input('status'); // watching|completed|plan_to_watch|dropped $query = Watchlist::where('user_id', $request->user()->id) ->with('anime:id,title,slug,cover_image,rating,episode_count,status,release_year'); if ($status) $query->where('status', $status); $items = $query->orderByDesc('updated_at')->paginate(24); return response()->json([ 'data' => collect($items->items())->map(fn($w) => [ 'id' => $w->id, 'status' => $w->status, 'anime' => $w->anime ? [ 'id' => $w->anime->id, 'title' => $w->anime->title, 'slug' => $w->anime->slug, 'cover_url' => \App\Support\MediaUrl::fromStoragePath($w->anime->cover_image), 'rating' => $w->anime->rating, 'episode_count' => $w->anime->episode_count, 'status' => $w->anime->status, 'release_year' => $w->anime->release_year, ] : null, ]), 'total' => $items->total(), 'last_page'=> $items->lastPage(), ]); } public function watchlistToggle(Request $request, Anime $anime) { $user = $request->user(); $status = $request->input('status', 'plan_to_watch'); $existing = Watchlist::where('user_id', $user->id)->where('anime_id', $anime->id)->first(); if ($existing) { if ($existing->status === $status) { $existing->delete(); return response()->json(['in_watchlist' => false, 'status' => null]); } $existing->update(['status' => $status]); return response()->json(['in_watchlist' => true, 'status' => $status]); } Watchlist::create(['user_id' => $user->id, 'anime_id' => $anime->id, 'status' => $status]); return response()->json(['in_watchlist' => true, 'status' => $status]); } // ── Continue Watching ────────────────────────────────────────────────────── public function continueWatchingUpdate(Request $request) { $data = $request->validate([ 'anime_id' => 'required|exists:animes,id', 'season_number' => 'required|integer|min:1', 'episode_number' => 'required|integer|min:1', 'percent_complete' => 'required|numeric|min:0|max:100', ]); ContinueWatching::updateOrCreate( ['user_id' => $request->user()->id, 'anime_id' => $data['anime_id']], [ 'season_number' => $data['season_number'], 'episode_number' => $data['episode_number'], 'percent_complete' => $data['percent_complete'], ] ); return response()->json(['ok' => true]); } // ── Anime Rate ───────────────────────────────────────────────────────────── public function animeRate(Request $request, Anime $anime) { $data = $request->validate(['rating' => 'required|numeric|min:1|max:10']); AnimeRating::updateOrCreate( ['user_id' => $request->user()->id, 'anime_id' => $anime->id], ['rating' => $data['rating']] ); $avg = AnimeRating::where('anime_id', $anime->id)->avg('rating'); $anime->update(['rating' => round($avg, 1)]); return response()->json(['rating' => $data['rating'], 'avg' => round($avg, 1)]); } // ── Follow ───────────────────────────────────────────────────────────────── public function followToggle(Request $request, Anime $anime) { $user = $request->user(); $existing = AnimeFollow::where('user_id', $user->id)->where('anime_id', $anime->id)->first(); if ($existing) { $existing->delete(); return response()->json(['following' => false]); } AnimeFollow::create(['user_id' => $user->id, 'anime_id' => $anime->id]); return response()->json(['following' => true]); } // ── Notifications ────────────────────────────────────────────────────────── public function notifications(Request $request) { $items = UserNotification::where('user_id', $request->user()->id) ->orderByDesc('created_at')->paginate(20); // Mark all as read UserNotification::where('user_id', $request->user()->id)->whereNull('read_at')->update(['read_at' => now()]); return response()->json([ 'data' => collect($items->items())->map(fn($n) => [ 'id' => $n->id, 'type' => $n->type, 'data' => $n->data ?? [], 'is_read' => $n->is_read, 'created_at' => $n->created_at?->diffForHumans(), ]), 'total' => $items->total(), 'last_page'=> $items->lastPage(), ]); } public function notificationsCount(Request $request) { if (!$request->user()) return response()->json(['count' => 0]); $count = UserNotification::where('user_id', $request->user()->id)->whereNull('read_at')->count(); return response()->json(['count' => $count]); } // ── Achievements ─────────────────────────────────────────────────────────── public function achievements(Request $request) { $all = Achievement::orderBy('points')->get(); $earned = UserAchievement::where('user_id', $request->user()->id)->pluck('achievement_id')->toArray(); $total = UserAchievement::where('user_id', $request->user()->id)->join('achievements','achievements.id','=','user_achievements.achievement_id')->sum('achievements.points'); return response()->json([ 'total_points' => (int)$total, 'data' => $all->map(fn($a) => [ 'id' => $a->id, 'name' => $a->name, 'description' => $a->description, 'icon' => $a->icon, 'points' => $a->points, 'earned' => in_array($a->id, $earned), 'earned_at' => in_array($a->id, $earned) ? UserAchievement::where('user_id', $request->user()->id)->where('achievement_id', $a->id)->value('created_at')?->toISOString() : null, ]), ]); } // ── Episode Notes ────────────────────────────────────────────────────────── public function noteStore(Request $request, $episodeId) { $data = $request->validate(['note' => 'required|string|max:1000']); $note = EpisodeNote::create([ 'user_id' => $request->user()->id, 'episode_id' => $episodeId, 'note' => $data['note'], ]); return response()->json(['id' => $note->id, 'note' => $note->note, 'created_at' => $note->created_at?->toISOString()], 201); } public function noteDelete(Request $request, $noteId) { $note = EpisodeNote::where('id', $noteId)->where('user_id', $request->user()->id)->firstOrFail(); $note->delete(); return response()->json(['ok' => true]); } public function episodeNotesList(Request $request, $episodeId) { $notes = EpisodeNote::where('user_id', $request->user()->id) ->where('episode_id', $episodeId) ->orderByDesc('created_at')->get(); return response()->json($notes->map(fn($n) => [ 'id' => $n->id, 'note' => $n->note, 'created_at' => $n->created_at?->toISOString(), ])); } // ── Anime Requests ───────────────────────────────────────────────────────── public function requestIndex(Request $request) { $items = AnimeRequest::withCount('votes') ->orderByDesc('votes_count')->orderByDesc('created_at')->paginate(20); return response()->json([ 'data' => collect($items->items())->map(fn($r) => [ 'id' => $r->id, 'title' => $r->title, 'note' => $r->note, 'status' => $r->status, 'votes_count' => $r->votes_count, 'created_at' => $r->created_at?->diffForHumans(), 'user_voted' => $request->user() ? AnimeRequestVote::where('user_id', $request->user()->id)->where('anime_request_id', $r->id)->exists() : false, ]), 'total' => $items->total(), 'last_page'=> $items->lastPage(), ]); } public function requestStore(Request $request) { $data = $request->validate([ 'title' => 'required|string|max:200', 'note' => 'nullable|string|max:500', ]); $req = AnimeRequest::create([ 'user_id' => $request->user()->id, 'title' => $data['title'], 'note' => $data['note'] ?? null, 'status' => 'pending', ]); return response()->json(['id' => $req->id, 'title' => $req->title], 201); } public function requestVote(Request $request, AnimeRequest $animeRequest) { $user = $request->user(); $existing = AnimeRequestVote::where('user_id', $user->id)->where('anime_request_id', $animeRequest->id)->first(); if ($existing) { $existing->delete(); return response()->json(['voted' => false, 'votes' => $animeRequest->votes()->count()]); } AnimeRequestVote::create(['user_id' => $user->id, 'anime_request_id' => $animeRequest->id]); return response()->json(['voted' => true, 'votes' => $animeRequest->votes()->count()]); } // ── Profile Stats ────────────────────────────────────────────────────────── public function profileStats(Request $request) { $userId = $request->user()->id; $watchlistCount = Watchlist::where('user_id', $userId)->count(); $completedCount = Watchlist::where('user_id', $userId)->where('status', 'completed')->count(); $notifCount = UserNotification::where('user_id', $userId)->where('is_read', false)->count(); $achPoints = UserAchievement::where('user_id', $userId) ->join('achievements','achievements.id','=','user_achievements.achievement_id') ->sum('achievements.points'); $achCount = UserAchievement::where('user_id', $userId)->count(); $commentCount = \App\Models\Comment::where('user_id', $userId)->count(); return response()->json([ 'watchlist_count' => $watchlistCount, 'completed_count' => $completedCount, 'notif_count' => (int)$notifCount, 'achievement_points'=> (int)$achPoints, 'achievement_count' => $achCount, 'comment_count' => $commentCount, ]); } }