import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model, Types } from 'mongoose';
import { ReactionType } from '../../common/enums/reaction-type.enum';
import { Like, LikeDocument } from './schemas/like.schema';

@Injectable()
export class LikesRepository {
  constructor(@InjectModel(Like.name) private readonly likeModel: Model<LikeDocument>) {}

  async findOne(userId: string, targetId: string, targetType: 'post' | 'comment'): Promise<LikeDocument | null> {
    return this.likeModel
      .findOne({
        userId: new Types.ObjectId(userId),
        targetId: new Types.ObjectId(targetId),
        targetType,
      })
      .exec();
  }

  async create(
    userId: string,
    targetId: string,
    targetType: 'post' | 'comment',
    reactionType: ReactionType = ReactionType.LIKE,
  ): Promise<LikeDocument> {
    return this.likeModel.create({
      userId: new Types.ObjectId(userId),
      targetId: new Types.ObjectId(targetId),
      targetType,
      reactionType,
    });
  }

  async createIfAbsent(
    userId: string,
    targetId: string,
    targetType: 'post' | 'comment',
    reactionType: ReactionType = ReactionType.LIKE,
  ): Promise<boolean> {
    const result = await this.likeModel
      .updateOne(
        {
          userId: new Types.ObjectId(userId),
          targetId: new Types.ObjectId(targetId),
          targetType,
        },
        { $setOnInsert: { reactionType } },
        { upsert: true },
      )
      .exec();
    return result.upsertedCount === 1;
  }

  async updateReaction(id: string, reactionType: ReactionType): Promise<LikeDocument | null> {
    return this.likeModel.findByIdAndUpdate(id, { reactionType }, { new: true }).exec();
  }

  async findLikedPostIds(userId: string, postIds: string[]): Promise<string[]> {
    if (!postIds.length) {
      return [];
    }

    const rows = await this.likeModel
      .find({
        userId: new Types.ObjectId(userId),
        targetType: 'post',
        targetId: { $in: postIds.map((id) => new Types.ObjectId(id)) },
      })
      .select({ targetId: 1 })
      .lean()
      .exec();

    return rows.map((row) => row.targetId.toString());
  }

  async deleteById(id: string): Promise<boolean> {
    const deleted = await this.likeModel.findByIdAndDelete(id).exec();
    return !!deleted;
  }

  async getReactionSummary(targetId: string, targetType: 'post' | 'comment') {
    const rows = await this.likeModel
      .aggregate<{ _id: ReactionType; count: number }>([
        {
          $match: {
            targetId: new Types.ObjectId(targetId),
            targetType,
          },
        },
        { $group: { _id: '$reactionType', count: { $sum: 1 } } },
      ])
      .exec();

    return Object.fromEntries(rows.map((row) => [row._id ?? ReactionType.LIKE, row.count]));
  }
}
