import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model, Types } from 'mongoose';
import { Block, BlockDocument } from './schemas/block.schema';

@Injectable()
export class BlocksRepository {
  constructor(@InjectModel(Block.name) private readonly blockModel: Model<BlockDocument>) {}

  async findOne(blockerId: string, blockedId: string): Promise<BlockDocument | null> {
    return this.blockModel
      .findOne({
        blockerId: new Types.ObjectId(blockerId),
        blockedId: new Types.ObjectId(blockedId),
      })
      .exec();
  }

  async findAnyBetween(userA: string, userB: string): Promise<BlockDocument | null> {
    return this.blockModel
      .findOne({
        $or: [
          { blockerId: new Types.ObjectId(userA), blockedId: new Types.ObjectId(userB) },
          { blockerId: new Types.ObjectId(userB), blockedId: new Types.ObjectId(userA) },
        ],
      })
      .exec();
  }

  async create(blockerId: string, blockedId: string): Promise<void> {
    await this.blockModel
      .updateOne(
        {
          blockerId: new Types.ObjectId(blockerId),
          blockedId: new Types.ObjectId(blockedId),
        },
        {
          $setOnInsert: {
            blockerId: new Types.ObjectId(blockerId),
            blockedId: new Types.ObjectId(blockedId),
          },
        },
        { upsert: true },
      )
      .exec();
  }

  async remove(blockerId: string, blockedId: string): Promise<void> {
    await this.blockModel
      .deleteOne({
        blockerId: new Types.ObjectId(blockerId),
        blockedId: new Types.ObjectId(blockedId),
      })
      .exec();
  }

  async findBlockedIds(blockerId: string): Promise<string[]> {
    const rows = await this.blockModel
      .find({ blockerId: new Types.ObjectId(blockerId) })
      .select({ blockedId: 1 })
      .lean()
      .exec();

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

  async findBlockingOrBlockedIds(userId: string): Promise<string[]> {
    const userObjectId = new Types.ObjectId(userId);
    const rows = await this.blockModel
      .find({ $or: [{ blockerId: userObjectId }, { blockedId: userObjectId }] })
      .select({ blockerId: 1, blockedId: 1 })
      .lean()
      .exec();

    return Array.from(
      new Set(
        rows.map((row) =>
          row.blockerId.toString() === userId ? row.blockedId.toString() : row.blockerId.toString(),
        ),
      ),
    );
  }
}
