File size: 5,416 Bytes
e557560
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// src/controllers/review.controller.js
const Review = require('../models/Review');
const Media = require('../models/Media');
const User = require('../models/User');

// --- Helper: Recalculate Average Rating ---
// We run this every time a review is added. 
// It uses MongoDB's math engine to average the scores instantly.
const updateMediaStats = async (mediaId) => {
  const stats = await Review.aggregate([
    { $match: { mediaId: mediaId } },
    {
      $group: {
        _id: '$mediaId',
        avgRating: { $avg: '$rating' },
        reviewCount: { $sum: 1 }
      }
    }
  ]);

  if (stats.length > 0) {
    await Media.findByIdAndUpdate(mediaId, {
      'brutaleStats.avgRating': stats[0].avgRating.toFixed(1),
      'brutaleStats.reviewCount': stats[0].reviewCount
    });
  }
};

// --- 1. POST: Create Review ---
exports.createReview = async (req, res) => {
  try {
    const { mediaId, rating, content, guestName } = req.body;

    // COERCE isSpoiler safely
    const isSpoiler = (() => {
      const val = req.body.isSpoiler;
      if (typeof val === 'boolean') return val;
      if (typeof val === 'string') return val === 'true';
      return false;
    })();

    const userId = req.user ? req.user.id : null;

    // A. Validation
    if (!mediaId || !rating || !content) {
      return res.status(400).json({ error: 'Missing required fields' });
    }

    // --- NEW: load username for logged-in user ---
    let finalGuestName = guestName;   // default for guests

    if (userId) {
      const user = await User.findById(userId).select("username");
      finalGuestName = user.username; // overwrite guestName
    } else {
      finalGuestName = guestName || "Anonymous Brutalist";
    }

    // B. Anti-Spam
    const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000);
    const duplicateCheck = await Review.findOne({
      mediaId,
      $or: [
        { userId: userId },
        { guestName: finalGuestName }
      ],
      createdAt: { $gte: tenMinutesAgo }
    });

    if (duplicateCheck) {
      return res.status(429).json({ error: 'Chill! You just reviewed this. Wait 10 mins.' });
    }

    // C. Build Review
    const reviewData = {
      mediaId,
      userId,
      rating,
      content,
      isSpoiler,
      guestName: finalGuestName,      // <-- ALWAYS included
      slangTags: []
    };

    const newReview = await Review.create(reviewData);

    // D. Update user stats
    if (userId) {
      await User.findByIdAndUpdate(userId, { $inc: { 'stats.reviewsCount': 1 } });
    }

    // E. Update media stats
    updateMediaStats(mediaId);

    res.status(201).json(newReview);

  } catch (error) {
    console.error('Review Error:', error);
    res.status(500).json({ error: 'Server crashed processing your review.' });
  }
};


// --- 2. GET: Fetch Reviews ---
exports.getReviews = async (req, res) => {
  try {
    const { mediaId } = req.params;
    const page = parseInt(req.query.page) || 1;
    const limit = 10;

    const reviews = await Review.find({ mediaId })
      .populate('userId', 'username honestyScore badges') // Get author details
      .sort({ createdAt: -1 }) // Newest first
      .skip((page - 1) * limit)
      .limit(limit);

    res.json(reviews);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch reviews' });
  }
};

// --- 3. GET: Reviews by User ---
exports.getReviewsByUser = async (req, res) => {
  try {
    const { userId } = req.params;

    const reviews = await Review.find({ userId })
      .populate('mediaId', 'title posterPath type') // bring media info
      .sort({ createdAt: -1 });

    res.json(reviews);

  } catch (error) {
    console.error("User Review Fetch Error:", error);
    res.status(500).json({ error: 'Failed to fetch user review history' });
  }
};

// -------------------
exports.addReply = async (req, res) => {
  try {
    const { id } = req.params; // Review ID
    const { content, guestName } = req.body;
    const userId = req.user ? req.user.id : null;

    if (!content) return res.status(400).json({ error: 'Content required' });

    // 1. Find Review
    const review = await Review.findById(id);
    if (!review) return res.status(404).json({ error: 'Review not found' });

    // 2. Determine reply name
    let finalGuestName;

    if (userId) {
      const user = await User.findById(userId).select("username");
      finalGuestName = user.username;     // logged in user = username
    } else {
      finalGuestName = guestName || "Anonymous Brutalist";  // guest user
    }

    // 3. Build reply object
    const newReply = {
      content,
      userId: userId || null,
      guestName: finalGuestName
    };

    // 4. Add reply and save
    review.replies.push(newReply);
    await review.save();

    // 5. Populate username immediately
    await review.populate("replies.userId", "username");

    // 6. Get the reply we just pushed
    let added = review.replies[review.replies.length - 1];

    // Normalize shape for frontend (safe, clean)
    const normalizedReply = {
      _id: added._id,
      content: added.content,
      createdAt: added.createdAt,
      username: added.userId ? added.userId.username : added.guestName,
      userId: added.userId?._id || null
    };

    res.status(201).json(normalizedReply);

  } catch (error) {
    console.error("Reply Error:", error);
    res.status(500).json({ error: 'Failed to add reply' });
  }
};