/**
 * Galaxy Strike Online — Social System (GDD §17.2)
 * Friends list, online status, sector tracking, gift system (≤5k credits/day)
 */

const mongoose = require('mongoose');

const socialSchema = new mongoose.Schema({
  userId:   { type: String, required: true, unique: true },
  username: { type: String, default: '' },
  friends:  [{ userId: String, username: String, addedAt: Date }],
  friendRequests: [{  // pending incoming
    fromId: String, fromName: String, sentAt: { type: Date, default: Date.now }
  }],
  giftsGivenToday: { type: Number, default: 0 },
  lastGiftReset:   { type: Date, default: Date.now },
  // Combat log entries (GDD §9.5) — last 50
  combatLog: [{ type: mongoose.Schema.Types.Mixed }],
}, { timestamps: true });

const SocialData = mongoose.model('SocialData', socialSchema);

const DAILY_GIFT_LIMIT = 5000;

class SocialSystem {
  constructor() { this.SocialData = SocialData; }

  async getOrCreate(userId, username) {
    let doc = await SocialData.findOne({ userId });
    if (!doc) { doc = new SocialData({ userId, username }); await doc.save(); }
    return doc;
  }

  async sendFriendRequest(senderId, senderName, targetUsername, onlinePlayers) {
    // Find target in online players first, then DB
    let targetId = null, targetName = null;
    for (const [, cd] of onlinePlayers) {
      if (cd.username?.toLowerCase() === targetUsername.toLowerCase()) {
        targetId = cd.userId; targetName = cd.username; break;
      }
    }
    if (!targetId) {
      const pd = await SocialData.findOne({ username: new RegExp('^' + targetUsername + '$', 'i') });
      if (pd) { targetId = pd.userId; targetName = pd.username; }
    }
    if (!targetId) throw new Error(`Player "${targetUsername}" not found`);
    if (targetId === senderId) throw new Error('Cannot add yourself');

    const targetDoc = await this.getOrCreate(targetId, targetName);
    if (targetDoc.friends.some(f => f.userId === senderId)) throw new Error('Already friends');
    if (targetDoc.friendRequests.some(r => r.fromId === senderId)) throw new Error('Request already sent');

    targetDoc.friendRequests.push({ fromId: senderId, fromName: senderName });
    await targetDoc.save();
    return { targetId, targetName };
  }

  async acceptFriendRequest(userId, username, fromId, fromName) {
    const myDoc     = await this.getOrCreate(userId, username);
    const theirDoc  = await this.getOrCreate(fromId, fromName);
    const idx = myDoc.friendRequests.findIndex(r => r.fromId === fromId);
    if (idx < 0) throw new Error('No pending request from that player');
    myDoc.friendRequests.splice(idx, 1);
    if (!myDoc.friends.some(f => f.userId === fromId))    myDoc.friends.push({ userId: fromId, username: fromName, addedAt: new Date() });
    if (!theirDoc.friends.some(f => f.userId === userId)) theirDoc.friends.push({ userId, username, addedAt: new Date() });
    await myDoc.save(); await theirDoc.save();
  }

  async removeFriend(userId, friendId) {
    await SocialData.updateOne({ userId }, { $pull: { friends: { userId: friendId } } });
    await SocialData.updateOne({ userId: friendId }, { $pull: { friends: { userId } } });
  }

  async sendGift(senderData, targetId, amount) {
    if (amount > DAILY_GIFT_LIMIT || amount <= 0) throw new Error(`Gift must be 1–${DAILY_GIFT_LIMIT} credits`);
    const myDoc = await this.getOrCreate(senderData.userId, senderData.username);
    // Reset daily counter if needed
    const today = new Date(); today.setHours(0,0,0,0);
    if (myDoc.lastGiftReset < today) { myDoc.giftsGivenToday = 0; myDoc.lastGiftReset = today; }
    if (myDoc.giftsGivenToday + amount > DAILY_GIFT_LIMIT) throw new Error(`Daily gift limit is ${DAILY_GIFT_LIMIT} credits`);
    if ((senderData.stats?.credits || 0) < amount) throw new Error('Insufficient credits');

    senderData.stats.credits -= amount;
    myDoc.giftsGivenToday += amount;
    await myDoc.save();
    return { targetId, amount };
  }

  async getFriendsList(userId, username, onlinePlayers) {
    const doc = await this.getOrCreate(userId, username);
    const onlineIds = new Set();
    for (const [, cd] of onlinePlayers) if (cd.userId) onlineIds.add(cd.userId);
    return {
      friends: doc.friends.map(f => ({ ...f.toObject?.() || f, online: onlineIds.has(f.userId) })),
      requests: doc.friendRequests,
    };
  }

  async addCombatLogEntry(userId, entry) {
    await SocialData.findOneAndUpdate(
      { userId },
      { $push: { combatLog: { $each: [entry], $slice: -50 } } },
      { upsert: true }
    );
  }

  async getCombatLog(userId) {
    const doc = await SocialData.findOne({ userId });
    return (doc?.combatLog || []).reverse();
  }
}

module.exports = SocialSystem;
